Merge remote-tracking branch 'origin/main' into vim-indent

This commit is contained in:
Nathan Sobo 2023-06-27 12:09:47 +02:00
commit 29b2639b4c
34 changed files with 1694 additions and 666 deletions

2
Cargo.lock generated
View File

@ -109,6 +109,8 @@ dependencies = [
"isahc",
"language",
"menu",
"project",
"regex",
"schemars",
"search",
"serde",

View File

@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9 0.499976C13.9 0.279062 13.7209 0.0999756 13.5 0.0999756C13.2791 0.0999756 13.1 0.279062 13.1 0.499976V1.09998H12.5C12.2791 1.09998 12.1 1.27906 12.1 1.49998C12.1 1.72089 12.2791 1.89998 12.5 1.89998H13.1V2.49998C13.1 2.72089 13.2791 2.89998 13.5 2.89998C13.7209 2.89998 13.9 2.72089 13.9 2.49998V1.89998H14.5C14.7209 1.89998 14.9 1.72089 14.9 1.49998C14.9 1.27906 14.7209 1.09998 14.5 1.09998H13.9V0.499976ZM11.8536 3.14642C12.0488 3.34168 12.0488 3.65826 11.8536 3.85353L10.8536 4.85353C10.6583 5.04879 10.3417 5.04879 10.1465 4.85353C9.9512 4.65827 9.9512 4.34169 10.1465 4.14642L11.1464 3.14643C11.3417 2.95116 11.6583 2.95116 11.8536 3.14642ZM9.85357 5.14642C10.0488 5.34168 10.0488 5.65827 9.85357 5.85353L2.85355 12.8535C2.65829 13.0488 2.34171 13.0488 2.14645 12.8535C1.95118 12.6583 1.95118 12.3417 2.14645 12.1464L9.14646 5.14642C9.34172 4.95116 9.65831 4.95116 9.85357 5.14642ZM13.5 5.09998C13.7209 5.09998 13.9 5.27906 13.9 5.49998V6.09998H14.5C14.7209 6.09998 14.9 6.27906 14.9 6.49998C14.9 6.72089 14.7209 6.89998 14.5 6.89998H13.9V7.49998C13.9 7.72089 13.7209 7.89998 13.5 7.89998C13.2791 7.89998 13.1 7.72089 13.1 7.49998V6.89998H12.5C12.2791 6.89998 12.1 6.72089 12.1 6.49998C12.1 6.27906 12.2791 6.09998 12.5 6.09998H13.1V5.49998C13.1 5.27906 13.2791 5.09998 13.5 5.09998ZM8.90002 0.499976C8.90002 0.279062 8.72093 0.0999756 8.50002 0.0999756C8.2791 0.0999756 8.10002 0.279062 8.10002 0.499976V1.09998H7.50002C7.2791 1.09998 7.10002 1.27906 7.10002 1.49998C7.10002 1.72089 7.2791 1.89998 7.50002 1.89998H8.10002V2.49998C8.10002 2.72089 8.2791 2.89998 8.50002 2.89998C8.72093 2.89998 8.90002 2.72089 8.90002 2.49998V1.89998H9.50002C9.72093 1.89998 9.90002 1.72089 9.90002 1.49998C9.90002 1.27906 9.72093 1.09998 9.50002 1.09998H8.90002V0.499976Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 7.5C1 7.22386 1.22386 7 1.5 7H13.5C13.7761 7 14 7.22386 14 7.5C14 7.77614 13.7761 8 13.5 8H1.5C1.22386 8 1 7.77614 1 7.5ZM1 11.5C1 11.2239 1.22386 11 1.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H1.5C1.22386 12 1 11.7761 1 11.5Z" fill="#CCCAC2"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.42503 3.44136C10.0561 3.23654 10.7837 3.2402 11.3792 3.54623C12.7532 4.25224 13.3477 6.07191 12.7946 8C12.5465 8.8649 12.1102 9.70472 11.1861 10.5524C10.262 11.4 8.98034 11.9 8.38571 11.9C8.17269 11.9 8 11.7321 8 11.525C8 11.3179 8.17644 11.15 8.38571 11.15C9.06497 11.15 9.67189 10.7804 10.3906 10.236C10.9406 9.8193 11.3701 9.28633 11.608 8.82191C12.0628 7.93367 12.0782 6.68174 11.3433 6.34901C10.9904 6.73455 10.5295 6.95946 9.97725 6.95946C8.7773 6.95946 8.0701 5.99412 8.10051 5.12009C8.12957 4.28474 8.66032 3.68954 9.42503 3.44136ZM3.42503 3.44136C4.05614 3.23654 4.78366 3.2402 5.37923 3.54623C6.7532 4.25224 7.34766 6.07191 6.79462 8C6.54654 8.8649 6.11019 9.70472 5.1861 10.5524C4.26201 11.4 2.98034 11.9 2.38571 11.9C2.17269 11.9 2 11.7321 2 11.525C2 11.3179 2.17644 11.15 2.38571 11.15C3.06497 11.15 3.67189 10.7804 4.39058 10.236C4.94065 9.8193 5.37014 9.28633 5.60797 8.82191C6.06282 7.93367 6.07821 6.68174 5.3433 6.34901C4.99037 6.73455 4.52948 6.95946 3.97725 6.95946C2.7773 6.95946 2.0701 5.99412 2.10051 5.12009C2.12957 4.28474 2.66032 3.68954 3.42503 3.44136Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.81832 0.68179C7.64258 0.506054 7.35766 0.506054 7.18192 0.68179L5.18192 2.68179C5.00619 2.85753 5.00619 3.14245 5.18192 3.31819C5.35766 3.49392 5.64258 3.49392 5.81832 3.31819L7.05012 2.08638L7.05012 5.50023C7.05012 5.74876 7.25159 5.95023 7.50012 5.95023C7.74865 5.95023 7.95012 5.74876 7.95012 5.50023L7.95012 2.08638L9.18192 3.31819C9.35766 3.49392 9.64258 3.49392 9.81832 3.31819C9.99406 3.14245 9.99406 2.85753 9.81832 2.68179L7.81832 0.68179ZM7.95012 12.9136V9.50023C7.95012 9.2517 7.74865 9.05023 7.50012 9.05023C7.25159 9.05023 7.05012 9.2517 7.05012 9.50023V12.9136L5.81832 11.6818C5.64258 11.5061 5.35766 11.5061 5.18192 11.6818C5.00619 11.8575 5.00619 12.1424 5.18192 12.3182L7.18192 14.3182C7.26632 14.4026 7.38077 14.45 7.50012 14.45C7.61947 14.45 7.73393 14.4026 7.81832 14.3182L9.81832 12.3182C9.99406 12.1424 9.99406 11.8575 9.81832 11.6818C9.64258 11.5061 9.35766 11.5061 9.18192 11.6818L7.95012 12.9136ZM1.49994 7.00017C1.2238 7.00017 0.999939 7.22403 0.999939 7.50017C0.999939 7.77631 1.2238 8.00017 1.49994 8.00017L13.4999 8.00017C13.7761 8.00017 13.9999 7.77631 13.9999 7.50017C13.9999 7.22403 13.7761 7.00017 13.4999 7.00017L1.49994 7.00017Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -40,7 +40,8 @@
"cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus"
"ctrl-`": "terminal_panel::ToggleFocus",
"shift-escape": "workspace::ToggleZoom"
}
},
{
@ -197,9 +198,17 @@
}
},
{
"context": "AssistantEditor > Editor",
"context": "AssistantPanel",
"bindings": {
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch"
}
},
{
"context": "ConversationEditor > Editor",
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-s": "workspace::Save",
"cmd->": "assistant::QuoteSelection",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole"
@ -234,8 +243,7 @@
"cmd-shift-g": "search::SelectPrevMatch",
"alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex",
"shift-escape": "workspace::ToggleZoom"
"alt-cmd-r": "search::ToggleRegex"
}
},
// Bindings from VS Code

View File

@ -1,6 +1,6 @@
[
{
"context": "Editor && VimControl && !VimWaiting",
"context": "Editor && VimControl && !VimWaiting && !menu",
"bindings": {
"g": [
"vim::PushOperator",
@ -58,10 +58,6 @@
}
],
"%": "vim::Matching",
"ctrl-y": [
"vim::Scroll",
"LineUp"
],
"f": [
"vim::PushOperator",
{
@ -197,33 +193,23 @@
"focus": true
}
],
"ctrl-f": [
"vim::Scroll",
"PageDown"
],
"ctrl-b": [
"vim::Scroll",
"PageUp"
],
"ctrl-d": [
"vim::Scroll",
"HalfPageDown"
],
"ctrl-u": [
"vim::Scroll",
"HalfPageUp"
],
"ctrl-e": [
"vim::Scroll",
"LineDown"
],
"ctrl-f": "vim::PageDown",
"pagedown": "vim::PageDown",
"ctrl-b": "vim::PageUp",
"pageup": "vim::PageUp",
"ctrl-d": "vim::ScrollDown",
"ctrl-u": "vim::ScrollUp",
"ctrl-e": "vim::LineDown",
"ctrl-y": "vim::LineUp",
"r": [
"vim::PushOperator",
"Replace"
],
"s": "vim::Substitute",
"> >": "editor::Indent",
"< <": "editor::Outdent"
"< <": "editor::Outdent",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem"
}
},
{
@ -240,6 +226,8 @@
"bindings": {
"g": "vim::StartOfDocument",
"h": "editor::Hover",
"t": "pane::ActivateNextItem",
"shift-t": "pane::ActivatePrevItem",
"escape": [
"vim::SwitchMode",
"Normal"

View File

@ -57,37 +57,37 @@
"show_whitespaces": "selection",
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
// When to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
},
"project_panel": {
// Whether to show the git status in the project panel.
"git_status": true,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
// Whether to show the git status in the project panel.
"git_status": true,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
},
"assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
"default_width": 450,
// Default height when the assistant is docked to the bottom.
"default_height": 320
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
"default_width": 640,
// Default height when the assistant is docked to the bottom.
"default_height": 320
},
// Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true,

View File

@ -22,9 +22,10 @@ util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow.workspace = true
chrono = "0.4"
chrono = { version = "0.4", features = ["serde"] }
futures.workspace = true
isahc.workspace = true
regex.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
@ -33,3 +34,4 @@ tiktoken-rs = "0.4"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }

View File

@ -1,10 +1,22 @@
pub mod assistant;
mod assistant_settings;
use anyhow::Result;
pub use assistant::AssistantPanel;
use chrono::{DateTime, Local};
use collections::HashMap;
use fs::Fs;
use futures::StreamExt;
use gpui::AppContext;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
use std::{
cmp::Reverse,
fmt::{self, Display},
path::PathBuf,
sync::Arc,
};
use util::paths::CONVERSATIONS_DIR;
// Data types for chat completion requests
#[derive(Debug, Serialize)]
@ -14,6 +26,84 @@ struct OpenAIRequest {
stream: bool,
}
#[derive(
Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
struct MessageId(usize);
#[derive(Clone, Debug, Serialize, Deserialize)]
struct MessageMetadata {
role: Role,
sent_at: DateTime<Local>,
status: MessageStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
enum MessageStatus {
Pending,
Done,
Error(Arc<str>),
}
#[derive(Serialize, Deserialize)]
struct SavedMessage {
id: MessageId,
start: usize,
}
#[derive(Serialize, Deserialize)]
struct SavedConversation {
zed: String,
version: String,
text: String,
messages: Vec<SavedMessage>,
message_metadata: HashMap<MessageId, MessageMetadata>,
summary: String,
model: String,
}
impl SavedConversation {
const VERSION: &'static str = "0.1.0";
}
struct SavedConversationMetadata {
title: String,
path: PathBuf,
mtime: chrono::DateTime<chrono::Local>,
}
impl SavedConversationMetadata {
pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
fs.create_dir(&CONVERSATIONS_DIR).await?;
let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
let mut conversations = Vec::<SavedConversationMetadata>::new();
while let Some(path) = paths.next().await {
let path = path?;
let pattern = r" - \d+.zed.json$";
let re = Regex::new(pattern).unwrap();
let metadata = fs.metadata(&path).await?;
if let Some((file_name, metadata)) = path
.file_name()
.and_then(|name| name.to_str())
.zip(metadata)
{
let title = re.replace(file_name, "");
conversations.push(Self {
title: title.into_owned(),
path,
mtime: metadata.mtime.into(),
});
}
}
conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
Ok(conversations)
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RequestMessage {
role: Role,

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use db::kvp::KEY_VALUE_STORE;
use gpui::{executor::Background, serde_json, AppContext, Task};
use lazy_static::lazy_static;
use parking_lot::Mutex;
@ -8,7 +7,6 @@ use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
use tempfile::NamedTempFile;
use util::http::HttpClient;
use util::{channel::ReleaseChannel, TryFutureExt};
use uuid::Uuid;
pub struct Telemetry {
http_client: Arc<dyn HttpClient>,
@ -120,39 +118,15 @@ impl Telemetry {
Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
}
pub fn start(self: &Arc<Self>) {
let this = self.clone();
self.executor
.spawn(
async move {
let installation_id =
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp("device_id") {
installation_id
} else {
let installation_id = Uuid::new_v4().to_string();
KEY_VALUE_STORE
.write_kvp("device_id".to_string(), installation_id.clone())
.await?;
installation_id
};
pub fn start(self: &Arc<Self>, installation_id: Option<String>) {
let mut state = self.state.lock();
state.installation_id = installation_id.map(|id| id.into());
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
drop(state);
let installation_id: Arc<str> = installation_id.into();
let mut state = this.state.lock();
state.installation_id = Some(installation_id.clone());
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
drop(state);
if has_clickhouse_events {
this.flush_clickhouse_events();
}
anyhow::Ok(())
}
.log_err(),
)
.detach();
if has_clickhouse_events {
self.flush_clickhouse_events();
}
}
/// This method takes the entire TelemetrySettings struct in order to force client code

View File

@ -7641,8 +7641,14 @@ impl View for Editor {
keymap.add_identifier("renaming");
}
match self.context_menu.as_ref() {
Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
Some(ContextMenu::Completions(_)) => {
keymap.add_identifier("menu");
keymap.add_identifier("showing_completions")
}
Some(ContextMenu::CodeActions(_)) => {
keymap.add_identifier("menu");
keymap.add_identifier("showing_code_actions")
}
None => {}
}
for layer in self.keymap_context_layers.values() {

View File

@ -1,5 +1,6 @@
use super::*;
use crate::{
scroll::scroll_amount::ScrollAmount,
test::{
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
editor_test_context::EditorTestContext, select_ranges,
@ -1359,6 +1360,43 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon
);
}
#[gpui::test]
async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
cx.simulate_window_resize(cx.window_id, vec2f(1000., 4. * line_height + 0.5));
cx.set_state(
&r#"ˇone
two
three
four
five
six
seven
eight
nine
ten
"#,
);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
editor.scroll_screen(&ScrollAmount::Page(1.), cx);
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
editor.scroll_screen(&ScrollAmount::Page(1.), cx);
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.));
editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
});
}
#[gpui::test]
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});

View File

@ -368,7 +368,7 @@ impl Editor {
}
let cur_position = self.scroll_position(cx);
let new_pos = cur_position + vec2f(0., amount.lines(self) - 1.);
let new_pos = cur_position + vec2f(0., amount.lines(self));
self.set_scroll_position(new_pos, cx);
}

View File

@ -27,22 +27,22 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::scroll_cursor_center);
cx.add_action(Editor::scroll_cursor_bottom);
cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
this.scroll_screen(&ScrollAmount::LineDown, cx)
this.scroll_screen(&ScrollAmount::Line(1.), cx)
});
cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
this.scroll_screen(&ScrollAmount::LineUp, cx)
this.scroll_screen(&ScrollAmount::Line(-1.), cx)
});
cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
this.scroll_screen(&ScrollAmount::HalfPageDown, cx)
this.scroll_screen(&ScrollAmount::Page(0.5), cx)
});
cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
this.scroll_screen(&ScrollAmount::HalfPageUp, cx)
this.scroll_screen(&ScrollAmount::Page(-0.5), cx)
});
cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
this.scroll_screen(&ScrollAmount::PageDown, cx)
this.scroll_screen(&ScrollAmount::Page(1.), cx)
});
cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
this.scroll_screen(&ScrollAmount::PageUp, cx)
this.scroll_screen(&ScrollAmount::Page(-1.), cx)
});
}

View File

@ -6,12 +6,10 @@ use crate::Editor;
#[derive(Clone, PartialEq, Deserialize)]
pub enum ScrollAmount {
LineUp,
LineDown,
HalfPageUp,
HalfPageDown,
PageUp,
PageDown,
// Scroll N lines (positive is towards the end of the document)
Line(f32),
// Scroll N pages (positive is towards the end of the document)
Page(f32),
}
impl ScrollAmount {
@ -24,10 +22,10 @@ impl ScrollAmount {
let context_menu = editor.context_menu.as_mut()?;
match self {
Self::LineDown | Self::HalfPageDown => context_menu.select_next(cx),
Self::LineUp | Self::HalfPageUp => context_menu.select_prev(cx),
Self::PageDown => context_menu.select_last(cx),
Self::PageUp => context_menu.select_first(cx),
Self::Line(c) if *c > 0. => context_menu.select_next(cx),
Self::Line(_) => context_menu.select_prev(cx),
Self::Page(c) if *c > 0. => context_menu.select_last(cx),
Self::Page(_) => context_menu.select_first(cx),
}
.then_some(())
})
@ -36,13 +34,13 @@ impl ScrollAmount {
pub fn lines(&self, editor: &mut Editor) -> f32 {
match self {
Self::LineDown => 1.,
Self::LineUp => -1.,
Self::HalfPageDown => editor.visible_line_count().map(|l| l / 2.).unwrap_or(1.),
Self::HalfPageUp => -editor.visible_line_count().map(|l| l / 2.).unwrap_or(1.),
// Minus 1. here so that there is a pivot line that stays on the screen
Self::PageDown => editor.visible_line_count().unwrap_or(1.) - 1.,
Self::PageUp => -editor.visible_line_count().unwrap_or(1.) - 1.,
Self::Line(count) => *count,
Self::Page(count) => editor
.visible_line_count()
// subtract one to leave an anchor line
// round towards zero (so page-up and page-down are symmetric)
.map(|l| ((l - 1.) * count).trunc())
.unwrap_or(0.),
}
}
}

View File

@ -165,6 +165,7 @@ impl<V: View> Element<V> for Label {
_: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
line.paint(
scene,
bounds.origin(),

View File

@ -1,7 +1,5 @@
use std::{borrow::Cow, ops::Range};
use serde_json::json;
use super::constrain_size_preserving_aspect_ratio;
use crate::json::ToJson;
use crate::{
color::Color,
geometry::{
@ -10,6 +8,10 @@ use crate::{
},
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
};
use schemars::JsonSchema;
use serde_derive::Deserialize;
use serde_json::json;
use std::{borrow::Cow, ops::Range};
pub struct Svg {
path: Cow<'static, str>,
@ -24,6 +26,14 @@ impl Svg {
}
}
pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
Self::new(style.asset)
.with_color(style.color)
.constrained()
.with_width(style.dimensions.width)
.with_height(style.dimensions.height)
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
@ -105,9 +115,24 @@ impl<V: View> Element<V> for Svg {
}
}
use crate::json::ToJson;
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct SvgStyle {
pub color: Color,
pub asset: String,
pub dimensions: Dimensions,
}
use super::constrain_size_preserving_aspect_ratio;
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct Dimensions {
pub width: f32,
pub height: f32,
}
impl Dimensions {
pub fn to_vec(&self) -> Vector2F {
vec2f(self.width, self.height)
}
}
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
RectF::new(

View File

@ -153,6 +153,7 @@ pub fn init(cx: &mut AppContext) {
);
}
#[derive(Debug)]
pub enum Event {
OpenedEntry {
entry_id: ProjectEntryId,

View File

@ -259,7 +259,11 @@ impl BufferSearchBar {
}
}
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
pub fn is_dismissed(&self) -> bool {
self.dismissed
}
pub fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
self.dismissed = true;
for searchable_item in self.seachable_items_with_matches.keys() {
if let Some(searchable_item) =
@ -275,7 +279,7 @@ impl BufferSearchBar {
cx.notify();
}
fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
} else {
@ -484,7 +488,7 @@ impl BufferSearchBar {
self.select_match(Direction::Prev, cx);
}
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
if let Some(index) = self.active_match_index {
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
if let Some(matches) = self

View File

@ -25,6 +25,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(TerminalPanel::new_terminal);
}
#[derive(Debug)]
pub enum Event {
Close,
DockPositionChanged,

View File

@ -4,7 +4,7 @@ pub mod ui;
use gpui::{
color::Color,
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
fonts::{HighlightStyle, TextStyle},
platform, AppContext, AssetSource, Border, MouseState,
};
@ -13,7 +13,7 @@ use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
use settings::SettingsStore;
use std::{collections::HashMap, sync::Arc};
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle};
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle};
pub use theme_registry::*;
pub use theme_settings::*;
@ -993,18 +993,33 @@ pub struct TerminalStyle {
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct AssistantStyle {
pub container: ContainerStyle,
pub header: ContainerStyle,
pub hamburger_button: Interactive<IconStyle>,
pub split_button: Interactive<IconStyle>,
pub assist_button: Interactive<IconStyle>,
pub quote_button: Interactive<IconStyle>,
pub zoom_in_button: Interactive<IconStyle>,
pub zoom_out_button: Interactive<IconStyle>,
pub plus_button: Interactive<IconStyle>,
pub title: ContainedText,
pub message_header: ContainerStyle,
pub sent_at: ContainedText,
pub user_sender: Interactive<ContainedText>,
pub assistant_sender: Interactive<ContainedText>,
pub system_sender: Interactive<ContainedText>,
pub model_info_container: ContainerStyle,
pub model: Interactive<ContainedText>,
pub remaining_tokens: ContainedText,
pub no_remaining_tokens: ContainedText,
pub error_icon: Icon,
pub api_key_editor: FieldEditor,
pub api_key_prompt: ContainedText,
pub saved_conversation: SavedConversation,
}
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct SavedConversation {
pub container: Interactive<ContainerStyle>,
pub saved_at: ContainedText,
pub title: ContainedText,
}
#[derive(Clone, Deserialize, Default, JsonSchema)]

View File

@ -1,13 +1,12 @@
use std::borrow::Cow;
use gpui::{
color::Color,
elements::{
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
MouseEventHandler, ParentElement, Stack, Svg,
ConstrainedBox, Container, ContainerStyle, Dimensions, Empty, Flex, KeystrokeLabel, Label,
MouseEventHandler, ParentElement, Stack, Svg, SvgStyle,
},
fonts::TextStyle,
geometry::vector::{vec2f, Vector2F},
geometry::vector::Vector2F,
platform,
platform::MouseButton,
scene::MouseClick,
@ -94,25 +93,6 @@ where
.with_cursor_style(platform::CursorStyle::PointingHand)
}
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct SvgStyle {
pub color: Color,
pub asset: String,
pub dimensions: Dimensions,
}
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct Dimensions {
pub width: f32,
pub height: f32,
}
impl Dimensions {
pub fn to_vec(&self) -> Vector2F {
vec2f(self.width, self.height)
}
}
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
Svg::new(style.asset.clone())
.with_color(style.color)
@ -123,8 +103,8 @@ pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
#[derive(Clone, Deserialize, Default, JsonSchema)]
pub struct IconStyle {
icon: SvgStyle,
container: ContainerStyle,
pub icon: SvgStyle,
pub container: ContainerStyle,
}
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {

View File

@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
lazy_static::lazy_static! {
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
pub static ref CONVERSATIONS_DIR: PathBuf = HOME.join(".config/zed/conversations");
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");

View File

@ -1,9 +1,10 @@
mod change;
mod delete;
mod scroll;
mod substitute;
mod yank;
use std::{borrow::Cow, cmp::Ordering, sync::Arc};
use std::{borrow::Cow, sync::Arc};
use crate::{
motion::Motion,
@ -13,14 +14,12 @@ use crate::{
};
use collections::{HashMap, HashSet};
use editor::{
display_map::ToDisplayPoint,
scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
Anchor, Bias, ClipboardSelection, DisplayPoint, Editor,
display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, Bias, ClipboardSelection,
DisplayPoint,
};
use gpui::{actions, impl_actions, AppContext, ViewContext, WindowContext};
use gpui::{actions, AppContext, ViewContext, WindowContext};
use language::{AutoindentMode, Point, SelectionGoal};
use log::error;
use serde::Deserialize;
use workspace::Workspace;
use self::{
@ -30,9 +29,6 @@ use self::{
yank::{yank_motion, yank_object},
};
#[derive(Clone, PartialEq, Deserialize)]
struct Scroll(ScrollAmount);
actions!(
vim,
[
@ -51,8 +47,6 @@ actions!(
]
);
impl_actions!(vim, [Scroll]);
pub fn init(cx: &mut AppContext) {
cx.add_action(insert_after);
cx.add_action(insert_first_non_whitespace);
@ -90,13 +84,8 @@ pub fn init(cx: &mut AppContext) {
})
});
cx.add_action(paste);
cx.add_action(|_: &mut Workspace, Scroll(amount): &Scroll, cx| {
Vim::update(cx, |vim, cx| {
vim.update_active_editor(cx, |editor, cx| {
scroll(editor, amount, cx);
})
})
});
scroll::init(cx);
}
pub fn normal_motion(
@ -393,46 +382,6 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext<Workspace>) {
});
}
fn scroll(editor: &mut Editor, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
editor.scroll_screen(amount, cx);
if should_move_cursor {
let selection_ordering = editor.newest_selection_on_screen(cx);
if selection_ordering.is_eq() {
return;
}
let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
visible_rows as u32
} else {
return;
};
let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
let top_anchor = editor.scroll_manager.anchor().anchor;
editor.change_selections(None, cx, |s| {
s.replace_cursors_with(|snapshot| {
let mut new_point = top_anchor.to_display_point(&snapshot);
match selection_ordering {
Ordering::Less => {
*new_point.row_mut() += scroll_margin_rows;
new_point = snapshot.clip_point(new_point, Bias::Right);
}
Ordering::Greater => {
*new_point.row_mut() += visible_rows - scroll_margin_rows as u32;
new_point = snapshot.clip_point(new_point, Bias::Left);
}
Ordering::Equal => unreachable!(),
}
vec![new_point]
})
});
}
}
pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| {
vim.update_active_editor(cx, |editor, cx| {

View File

@ -0,0 +1,120 @@
use std::cmp::Ordering;
use crate::Vim;
use editor::{display_map::ToDisplayPoint, scroll::scroll_amount::ScrollAmount, Editor};
use gpui::{actions, AppContext, ViewContext};
use language::Bias;
use workspace::Workspace;
actions!(
vim,
[LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown,]
);
pub fn init(cx: &mut AppContext) {
cx.add_action(|_: &mut Workspace, _: &LineDown, cx| {
scroll(cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
});
cx.add_action(|_: &mut Workspace, _: &LineUp, cx| {
scroll(cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
});
cx.add_action(|_: &mut Workspace, _: &PageDown, cx| {
scroll(cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
});
cx.add_action(|_: &mut Workspace, _: &PageUp, cx| {
scroll(cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
});
cx.add_action(|_: &mut Workspace, _: &ScrollDown, cx| {
scroll(cx, |c| {
if let Some(c) = c {
ScrollAmount::Line(c)
} else {
ScrollAmount::Page(0.5)
}
})
});
cx.add_action(|_: &mut Workspace, _: &ScrollUp, cx| {
scroll(cx, |c| {
if let Some(c) = c {
ScrollAmount::Line(-c)
} else {
ScrollAmount::Page(-0.5)
}
})
});
}
fn scroll(cx: &mut ViewContext<Workspace>, by: fn(c: Option<f32>) -> ScrollAmount) {
Vim::update(cx, |vim, cx| {
let amount = by(vim.pop_number_operator(cx).map(|c| c as f32));
vim.update_active_editor(cx, |editor, cx| scroll_editor(editor, &amount, cx));
})
}
fn scroll_editor(editor: &mut Editor, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
editor.scroll_screen(amount, cx);
if should_move_cursor {
let selection_ordering = editor.newest_selection_on_screen(cx);
if selection_ordering.is_eq() {
return;
}
let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
visible_rows as u32
} else {
return;
};
let top_anchor = editor.scroll_manager.anchor().anchor;
editor.change_selections(None, cx, |s| {
s.replace_cursors_with(|snapshot| {
let mut new_point = top_anchor.to_display_point(&snapshot);
match selection_ordering {
Ordering::Less => {
new_point = snapshot.clip_point(new_point, Bias::Right);
}
Ordering::Greater => {
*new_point.row_mut() += visible_rows - 1;
new_point = snapshot.clip_point(new_point, Bias::Left);
}
Ordering::Equal => unreachable!(),
}
vec![new_point]
})
});
}
}
#[cfg(test)]
mod test {
use crate::{state::Mode, test::VimTestContext};
use gpui::geometry::vector::vec2f;
use indoc::indoc;
#[gpui::test]
async fn test_scroll(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(indoc! {"ˇa\nb\nc\nd\ne\n"}, Mode::Normal);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
});
cx.simulate_keystrokes(["ctrl-e"]);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.))
});
cx.simulate_keystrokes(["2", "ctrl-e"]);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.))
});
cx.simulate_keystrokes(["ctrl-y"]);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.))
});
}
}

View File

@ -249,7 +249,7 @@ impl Dock {
}
}
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
pub(crate) fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, |this, panel, event, cx| {
@ -605,6 +605,7 @@ pub mod test {
use super::*;
use gpui::{ViewContext, WindowContext};
#[derive(Debug)]
pub enum TestPanelEvent {
PositionChanged,
Activated,

View File

@ -1,9 +1,10 @@
mod dragged_item_receiver;
use super::{ItemHandle, SplitDirection};
pub use crate::toolbar::Toolbar;
use crate::{
item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item,
NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
item::WeakItemHandle, notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile,
NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
@ -250,7 +251,7 @@ impl Pane {
pane: handle.clone(),
next_timestamp,
}))),
toolbar: cx.add_view(|_| Toolbar::new(handle)),
toolbar: cx.add_view(|_| Toolbar::new(Some(handle))),
tab_bar_context_menu: TabBarContextMenu {
kind: TabBarContextMenuKind::New,
handle: context_menu,
@ -1112,7 +1113,7 @@ impl Pane {
.get(self.active_item_index)
.map(|item| item.as_ref());
self.toolbar.update(cx, |toolbar, cx| {
toolbar.set_active_pane_item(active_item, cx);
toolbar.set_active_item(active_item, cx);
});
}
@ -1602,7 +1603,7 @@ impl View for Pane {
}
self.toolbar.update(cx, |toolbar, cx| {
toolbar.pane_focus_update(true, cx);
toolbar.focus_changed(true, cx);
});
if let Some(active_item) = self.active_item() {
@ -1631,7 +1632,7 @@ impl View for Pane {
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.has_focus = false;
self.toolbar.update(cx, |toolbar, cx| {
toolbar.pane_focus_update(false, cx);
toolbar.focus_changed(false, cx);
});
cx.notify();
}

View File

@ -38,7 +38,7 @@ trait ToolbarItemViewHandle {
active_pane_item: Option<&dyn ItemHandle>,
cx: &mut WindowContext,
) -> ToolbarItemLocation;
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext);
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
fn row_count(&self, cx: &WindowContext) -> usize;
}
@ -51,10 +51,10 @@ pub enum ToolbarItemLocation {
}
pub struct Toolbar {
active_pane_item: Option<Box<dyn ItemHandle>>,
active_item: Option<Box<dyn ItemHandle>>,
hidden: bool,
can_navigate: bool,
pane: WeakViewHandle<Pane>,
pane: Option<WeakViewHandle<Pane>>,
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
}
@ -121,7 +121,7 @@ impl View for Toolbar {
let pane = self.pane.clone();
let mut enable_go_backward = false;
let mut enable_go_forward = false;
if let Some(pane) = pane.upgrade(cx) {
if let Some(pane) = pane.and_then(|pane| pane.upgrade(cx)) {
let pane = pane.read(cx);
enable_go_backward = pane.can_navigate_backward();
enable_go_forward = pane.can_navigate_forward();
@ -143,19 +143,17 @@ impl View for Toolbar {
enable_go_backward,
spacing,
{
let pane = pane.clone();
move |toolbar, cx| {
if let Some(workspace) = toolbar
.pane
.upgrade(cx)
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
{
let pane = pane.clone();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_back(pane.clone(), cx).detach_and_log_err(cx);
});
})
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
let pane = pane.downgrade();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_back(pane, cx).detach_and_log_err(cx);
});
})
}
}
}
},
@ -171,21 +169,17 @@ impl View for Toolbar {
enable_go_forward,
spacing,
{
let pane = pane.clone();
move |toolbar, cx| {
if let Some(workspace) = toolbar
.pane
.upgrade(cx)
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
{
let pane = pane.clone();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace
.go_forward(pane.clone(), cx)
.detach_and_log_err(cx);
});
});
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
let pane = pane.downgrade();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_forward(pane, cx).detach_and_log_err(cx);
});
})
}
}
}
},
@ -269,9 +263,9 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
}
impl Toolbar {
pub fn new(pane: WeakViewHandle<Pane>) -> Self {
pub fn new(pane: Option<WeakViewHandle<Pane>>) -> Self {
Self {
active_pane_item: None,
active_item: None,
pane,
items: Default::default(),
hidden: false,
@ -288,7 +282,7 @@ impl Toolbar {
where
T: 'static + ToolbarItemView,
{
let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
cx.subscribe(&item, |this, item, event, cx| {
if let Some((_, current_location)) =
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
@ -307,20 +301,16 @@ impl Toolbar {
cx.notify();
}
pub fn set_active_pane_item(
&mut self,
pane_item: Option<&dyn ItemHandle>,
cx: &mut ViewContext<Self>,
) {
self.active_pane_item = pane_item.map(|item| item.boxed_clone());
pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
self.active_item = item.map(|item| item.boxed_clone());
self.hidden = self
.active_pane_item
.active_item
.as_ref()
.map(|item| !item.show_toolbar(cx))
.unwrap_or(false);
for (toolbar_item, current_location) in self.items.iter_mut() {
let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
let new_location = toolbar_item.set_active_pane_item(item, cx);
if new_location != *current_location {
*current_location = new_location;
cx.notify();
@ -328,9 +318,9 @@ impl Toolbar {
}
}
pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut ViewContext<Self>) {
pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
for (toolbar_item, _) in self.items.iter_mut() {
toolbar_item.pane_focus_update(pane_focused, cx);
toolbar_item.focus_changed(focused, cx);
}
}
@ -364,7 +354,7 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
})
}
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext) {
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
self.update(cx, |this, cx| {
this.pane_focus_update(pane_focused, cx);
cx.notify();

View File

@ -861,7 +861,10 @@ impl Workspace {
&self.right_dock
}
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
where
T::Event: std::fmt::Debug,
{
let dock = match panel.position(cx) {
DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock,
@ -904,10 +907,11 @@ impl Workspace {
});
} else if T::should_zoom_in_on_event(event) {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
if panel.has_focus(cx) {
this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
if !panel.has_focus(cx) {
cx.focus(&panel);
}
this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
} else if T::should_zoom_out_on_event(event) {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
if this.zoomed_position == Some(prev_position) {
@ -1702,6 +1706,11 @@ impl Workspace {
cx.notify();
}
#[cfg(any(test, feature = "test-support"))]
pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
self.zoomed.and_then(|view| view.upgrade(cx))
}
fn dismiss_zoomed_items_to_reveal(
&mut self,
dock_to_reveal: Option<DockPosition>,

View File

@ -48,6 +48,7 @@ use util::{
http::{self, HttpClient},
paths::PathLikeWithPosition,
};
use uuid::Uuid;
use welcome::{show_welcome_experience, FIRST_OPEN};
use fs::RealFs;
@ -68,7 +69,8 @@ fn main() {
log::info!("========== starting zed ==========");
let mut app = gpui::App::new(Assets).unwrap();
init_panic_hook(&app);
let installation_id = app.background().block(installation_id()).ok();
init_panic_hook(&app, installation_id.clone());
app.background();
@ -168,7 +170,7 @@ fn main() {
})
.detach();
client.telemetry().start();
client.telemetry().start(installation_id);
let app_state = Arc::new(AppState {
languages,
@ -268,6 +270,22 @@ fn main() {
});
}
async fn installation_id() -> Result<String> {
let legacy_key_name = "device_id";
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
Ok(installation_id)
} else {
let installation_id = Uuid::new_v4().to_string();
KEY_VALUE_STORE
.write_kvp(legacy_key_name.to_string(), installation_id.clone())
.await?;
Ok(installation_id)
}
}
fn open_urls(
urls: Vec<String>,
cli_connections_tx: &mpsc::UnboundedSender<(
@ -371,6 +389,8 @@ struct Panic {
os_version: Option<String>,
architecture: String,
panicked_on: u128,
#[serde(skip_serializing_if = "Option::is_none")]
installation_id: Option<String>,
}
#[derive(Serialize)]
@ -379,7 +399,7 @@ struct PanicRequest {
token: String,
}
fn init_panic_hook(app: &App) {
fn init_panic_hook(app: &App, installation_id: Option<String>) {
let is_pty = stdout_is_a_pty();
let platform = app.platform();
@ -432,6 +452,7 @@ fn init_panic_hook(app: &App) {
.unwrap()
.as_millis(),
backtrace,
installation_id: installation_id.clone(),
};
if is_pty {

View File

@ -10,11 +10,190 @@ export default function assistant(colorScheme: ColorScheme) {
background: editor(colorScheme).background,
padding: { left: 12 },
},
header: {
messageHeader: {
border: border(layer, "default", { bottom: true, top: true }),
margin: { bottom: 6, top: 6 },
background: editor(colorScheme).background,
},
hamburgerButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/hamburger_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
splitButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/split_message_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
quoteButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/quote_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
assistButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/assist_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12, right: 24 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
zoomInButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/maximize_8.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
margin: { right: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
zoomOutButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/minimize_8.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
margin: { right: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
plusButton: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
asset: "icons/plus_12.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
margin: { right: 12 },
}
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
}),
title: {
margin: { left: 12 },
...text(layer, "sans", "default", { size: "sm" })
},
savedConversation: {
container: interactive({
base: {
background: background(layer, "on"),
padding: { top: 4, bottom: 4 }
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
}
},
}),
savedAt: {
margin: { left: 8 },
...text(layer, "sans", "default", { size: "xs" }),
},
title: {
margin: { left: 16 },
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
}
},
userSender: {
default: {
...text(layer, "sans", "default", {
@ -43,13 +222,10 @@ export default function assistant(colorScheme: ColorScheme) {
margin: { top: 2, left: 8 },
...text(layer, "sans", "default", { size: "2xs" }),
},
modelInfoContainer: {
margin: { right: 16, top: 4 },
},
model: interactive({
base: {
background: background(layer, "on"),
border: border(layer, "on", { overlay: true }),
margin: { left: 12, right: 12, top: 12 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "default", { size: "xs" }),
@ -57,22 +233,21 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
background: background(layer, "on", "hovered"),
border: border(layer, "on", { overlay: true }),
},
},
}),
remainingTokens: {
background: background(layer, "on"),
border: border(layer, "on", { overlay: true }),
margin: { top: 12, right: 12 },
padding: 4,
margin: { left: 4 },
cornerRadius: 4,
...text(layer, "sans", "positive", { size: "xs" }),
},
noRemainingTokens: {
background: background(layer, "on"),
border: border(layer, "on", { overlay: true }),
margin: { top: 12, right: 12 },
padding: 4,
margin: { left: 4 },
cornerRadius: 4,
...text(layer, "sans", "negative", { size: "xs" }),
},

View File

@ -30,7 +30,7 @@ const getTheme = (variant: Variant): ThemeConfig => {
return {
name: `${meta.name} Forest Light`,
author: meta.author,
appearance: ThemeAppearance.Dark,
appearance: ThemeAppearance.Light,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,