From 80080a43e4ad52a3596b69346e97005d21f48500 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 26 May 2023 10:09:55 -0600 Subject: [PATCH 001/131] Start on an assistant panel based on multi-buffers Each message is represented as a multibuffer excerpt to allow for fluid editing of the conversation transcript. Co-Authored-By: Antonio Scandurra --- Cargo.lock | 4 + assets/icons/speech_bubble_12.svg | 4 +- assets/keymaps/default.json | 36 ++-- crates/ai/Cargo.toml | 4 + crates/ai/src/ai.rs | 44 +++-- crates/ai/src/assistant.rs | 316 ++++++++++++++++++++++++++++++ crates/zed/src/zed.rs | 7 +- 7 files changed, 381 insertions(+), 34 deletions(-) create mode 100644 crates/ai/src/assistant.rs diff --git a/Cargo.lock b/Cargo.lock index 95fcf2224d..27c3a2fdb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,9 +106,13 @@ dependencies = [ "futures 0.3.28", "gpui", "isahc", + "language", + "search", "serde", "serde_json", + "theme", "util", + "workspace", ] [[package]] diff --git a/assets/icons/speech_bubble_12.svg b/assets/icons/speech_bubble_12.svg index f5f330056a..736f39a984 100644 --- a/assets/icons/speech_bubble_12.svg +++ b/assets/icons/speech_bubble_12.svg @@ -1,3 +1,3 @@ - - + + diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 35182dfaa6..5102b7408b 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -188,12 +188,6 @@ "alt-[": "copilot::PreviousSuggestion" } }, - { - "context": "Editor && extension == zmd", - "bindings": { - "cmd-enter": "ai::Assist" - } - }, { "context": "Editor && mode == auto_height", "bindings": { @@ -201,6 +195,12 @@ "cmd-alt-enter": "editor::NewlineBelow" } }, + { + "context": "ContextEditor > Editor", + "bindings": { + "cmd-enter": "assistant::Assist" + } + }, { "context": "BufferSearchBar", "bindings": { @@ -375,27 +375,39 @@ ], "cmd-b": [ "workspace::ToggleLeftDock", - { "focus": true } + { + "focus": true + } ], "cmd-shift-b": [ "workspace::ToggleLeftDock", - { "focus": false } + { + "focus": false + } ], "cmd-r": [ "workspace::ToggleRightDock", - { "focus": true } + { + "focus": true + } ], "cmd-shift-r": [ "workspace::ToggleRightDock", - { "focus": false } + { + "focus": false + } ], "cmd-j": [ "workspace::ToggleBottomDock", - { "focus": true } + { + "focus": true + } ], "cmd-shift-j": [ "workspace::ToggleBottomDock", - { "focus": false } + { + "focus": false + } ], "cmd-shift-f": "workspace::NewSearch", "cmd-k cmd-t": "theme_selector::Toggle", diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index b367a4d43c..14817916f4 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -13,7 +13,11 @@ assets = { path = "../assets"} collections = { path = "../collections"} editor = { path = "../editor" } gpui = { path = "../gpui" } +language = { path = "../language" } +search = { path = "../search" } +theme = { path = "../theme" } util = { path = "../util" } +workspace = { path = "../workspace" } serde.workspace = true serde_json.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index c68f41c6bf..cd42ee1153 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,3 +1,5 @@ +mod assistant; + use anyhow::{anyhow, Result}; use assets::Assets; use collections::HashMap; @@ -16,6 +18,8 @@ use std::{io, sync::Arc}; use util::channel::{ReleaseChannel, RELEASE_CHANNEL}; use util::{ResultExt, TryFutureExt}; +pub use assistant::AssistantPanel; + actions!(ai, [Assist]); // Data types for chat completion requests @@ -38,7 +42,7 @@ struct ResponseMessage { content: Option, } -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "lowercase")] enum Role { User, @@ -86,25 +90,27 @@ struct OpenAIChoice { } pub fn init(cx: &mut AppContext) { - if *RELEASE_CHANNEL == ReleaseChannel::Stable { - return; - } + // if *RELEASE_CHANNEL == ReleaseChannel::Stable { + // return; + // } - let assistant = Rc::new(Assistant::default()); - cx.add_action({ - let assistant = assistant.clone(); - move |editor: &mut Editor, _: &Assist, cx: &mut ViewContext| { - assistant.assist(editor, cx).log_err(); - } - }); - cx.capture_action({ - let assistant = assistant.clone(); - move |_: &mut Editor, _: &editor::Cancel, cx: &mut ViewContext| { - if !assistant.cancel_last_assist(cx.view_id()) { - cx.propagate_action(); - } - } - }); + assistant::init(cx); + + // let assistant = Rc::new(Assistant::default()); + // cx.add_action({ + // let assistant = assistant.clone(); + // move |editor: &mut Editor, _: &Assist, cx: &mut ViewContext| { + // assistant.assist(editor, cx).log_err(); + // } + // }); + // cx.capture_action({ + // let assistant = assistant.clone(); + // move |_: &mut Editor, _: &editor::Cancel, cx: &mut ViewContext| { + // if !assistant.cancel_last_assist(cx.view_id()) { + // cx.propagate_action(); + // } + // } + // }); } type CompletionId = usize; diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs new file mode 100644 index 0000000000..ef202e7214 --- /dev/null +++ b/crates/ai/src/assistant.rs @@ -0,0 +1,316 @@ +use crate::{stream_completion, OpenAIRequest, RequestMessage, Role}; +use editor::{Editor, ExcerptRange, MultiBuffer}; +use futures::StreamExt; +use gpui::{ + actions, elements::*, Action, AppContext, Entity, ModelHandle, Subscription, View, ViewContext, + ViewHandle, WeakViewHandle, WindowContext, +}; +use language::{language_settings::SoftWrap, Anchor, Buffer}; +use std::sync::Arc; +use util::ResultExt; +use workspace::{ + dock::{DockPosition, Panel}, + item::Item, + pane, Pane, Workspace, +}; + +actions!(assistant, [NewContext, Assist]); + +pub fn init(cx: &mut AppContext) { + cx.add_action(ContextEditor::assist); +} + +pub enum AssistantPanelEvent { + ZoomIn, + ZoomOut, + Focus, + Close, +} + +pub struct AssistantPanel { + width: Option, + pane: ViewHandle, + workspace: WeakViewHandle, + _subscriptions: Vec, +} + +impl AssistantPanel { + pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + let weak_self = cx.weak_handle(); + let pane = cx.add_view(|cx| { + let window_id = cx.window_id(); + let mut pane = Pane::new( + workspace.weak_handle(), + workspace.app_state().background_actions, + Default::default(), + cx, + ); + pane.set_can_split(false, cx); + pane.set_can_navigate(false, cx); + pane.on_can_drop(move |_, cx| false); + pane.set_render_tab_bar_buttons(cx, move |pane, cx| { + let this = weak_self.clone(); + Flex::row() + .with_child(Pane::render_tab_bar_button( + 0, + "icons/plus_12.svg", + Some(("New Context".into(), Some(Box::new(NewContext)))), + cx, + move |_, cx| {}, + None, + )) + .with_child(Pane::render_tab_bar_button( + 1, + if pane.is_zoomed() { + "icons/minimize_8.svg" + } else { + "icons/maximize_8.svg" + }, + Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), + cx, + move |pane, cx| pane.toggle_zoom(&Default::default(), cx), + None, + )) + .into_any() + }); + let buffer_search_bar = cx.add_view(search::BufferSearchBar::new); + pane.toolbar() + .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); + pane + }); + let subscriptions = vec![ + cx.observe(&pane, |_, _, cx| cx.notify()), + cx.subscribe(&pane, Self::handle_pane_event), + ]; + + Self { + pane, + workspace: workspace.weak_handle(), + width: None, + _subscriptions: subscriptions, + } + } + + fn handle_pane_event( + &mut self, + _pane: ViewHandle, + event: &pane::Event, + cx: &mut ViewContext, + ) { + match event { + pane::Event::ZoomIn => cx.emit(AssistantPanelEvent::ZoomIn), + pane::Event::ZoomOut => cx.emit(AssistantPanelEvent::ZoomOut), + pane::Event::Focus => cx.emit(AssistantPanelEvent::Focus), + pane::Event::Remove => cx.emit(AssistantPanelEvent::Close), + _ => {} + } + } +} + +impl Entity for AssistantPanel { + type Event = AssistantPanelEvent; +} + +impl View for AssistantPanel { + fn ui_name() -> &'static str { + "AssistantPanel" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + ChildView::new(&self.pane, cx).into_any() + } +} + +impl Panel for AssistantPanel { + fn position(&self, cx: &WindowContext) -> DockPosition { + DockPosition::Right + } + + fn position_is_valid(&self, position: DockPosition) -> bool { + matches!(position, DockPosition::Right) + } + + fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) {} + + fn size(&self, cx: &WindowContext) -> f32 { + self.width.unwrap_or(480.) + } + + fn set_size(&mut self, size: f32, cx: &mut ViewContext) { + self.width = Some(size); + cx.notify(); + } + + fn should_zoom_in_on_event(event: &AssistantPanelEvent) -> bool { + matches!(event, AssistantPanelEvent::ZoomIn) + } + + fn should_zoom_out_on_event(event: &AssistantPanelEvent) -> bool { + matches!(event, AssistantPanelEvent::ZoomOut) + } + + fn is_zoomed(&self, cx: &WindowContext) -> bool { + self.pane.read(cx).is_zoomed() + } + + fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext) { + self.pane.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx)); + } + + fn set_active(&mut self, active: bool, cx: &mut ViewContext) { + if active && self.pane.read(cx).items_len() == 0 { + cx.defer(|this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + let focus = this.pane.read(cx).has_focus(); + let editor = Box::new(cx.add_view(|cx| ContextEditor::new(cx))); + Pane::add_item(workspace, &this.pane, editor, true, focus, None, cx); + }) + } + }); + } + } + + fn icon_path(&self) -> &'static str { + "icons/speech_bubble_12.svg" + } + + fn icon_tooltip(&self) -> (String, Option>) { + ("Assistant Panel".into(), None) + } + + fn should_change_position_on_event(event: &Self::Event) -> bool { + false + } + + fn should_activate_on_event(_: &Self::Event) -> bool { + false + } + + fn should_close_on_event(event: &AssistantPanelEvent) -> bool { + matches!(event, AssistantPanelEvent::Close) + } + + fn has_focus(&self, cx: &WindowContext) -> bool { + self.pane.read(cx).has_focus() + } + + fn is_focus_event(event: &Self::Event) -> bool { + matches!(event, AssistantPanelEvent::Focus) + } +} + +struct ContextEditor { + messages: Vec, + editor: ViewHandle, +} + +impl ContextEditor { + fn new(cx: &mut ViewContext) -> Self { + let messages = vec![Message { + role: Role::User, + content: cx.add_model(|cx| Buffer::new(0, "", cx)), + }]; + + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + for message in &messages { + multibuffer.push_excerpts_with_context_lines( + message.content.clone(), + vec![Anchor::MIN..Anchor::MAX], + 0, + cx, + ); + } + multibuffer + }); + let editor = cx.add_view(|cx| { + let mut editor = Editor::for_multibuffer(multibuffer, None, cx); + editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); + editor + }); + + Self { messages, editor } + } + + fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { + let messages = self + .messages + .iter() + .map(|message| RequestMessage { + role: message.role, + content: message.content.read(cx).text(), + }) + .collect(); + let request = OpenAIRequest { + model: "gpt-3.5-turbo".into(), + messages, + stream: true, + }; + + if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() { + let stream = stream_completion(api_key, cx.background_executor().clone(), request); + let content = cx.add_model(|cx| Buffer::new(0, "", cx)); + self.messages.push(Message { + role: Role::Assistant, + content: content.clone(), + }); + self.editor.update(cx, |editor, cx| { + editor.buffer().update(cx, |multibuffer, cx| { + multibuffer.push_excerpts_with_context_lines( + content.clone(), + vec![Anchor::MIN..Anchor::MAX], + 0, + cx, + ); + }); + }); + cx.spawn(|_, mut cx| async move { + let mut messages = stream.await?; + + while let Some(message) = messages.next().await { + let mut message = message?; + if let Some(choice) = message.choices.pop() { + content.update(&mut cx, |content, cx| { + let text: Arc = choice.delta.content?.into(); + content.edit([(content.len()..content.len(), text)], None, cx); + Some(()) + }); + } + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + } +} + +impl Entity for ContextEditor { + type Event = (); +} + +impl View for ContextEditor { + fn ui_name() -> &'static str { + "ContextEditor" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + ChildView::new(&self.editor, cx).into_any() + } +} + +impl Item for ContextEditor { + fn tab_content( + &self, + _: Option, + style: &theme::Tab, + _: &gpui::AppContext, + ) -> AnyElement { + Label::new("New Context", style.label.clone()).into_any() + } +} + +struct Message { + role: Role, + content: ModelHandle, +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 619dd81a80..24b7d6356b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,6 +2,7 @@ pub mod languages; pub mod menus; #[cfg(any(test, feature = "test-support"))] pub mod test; +use ai::AssistantPanel; use anyhow::Context; use assets::Assets; use breadcrumbs::Breadcrumbs; @@ -357,7 +358,11 @@ pub fn initialize_workspace( workspace.toggle_dock(project_panel_position, false, cx); } - workspace.add_panel(terminal_panel, cx) + workspace.add_panel(terminal_panel, cx); + + // TODO: deserialize state. + let assistant_panel = cx.add_view(|cx| AssistantPanel::new(workspace, cx)); + workspace.add_panel(assistant_panel, cx); })?; Ok(()) }) From 8f6e67f440082e8a9807e5f0189556f922fb3183 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 26 May 2023 14:49:27 -0600 Subject: [PATCH 002/131] Cancel assists on escape --- assets/keymaps/default.json | 3 +- crates/ai/src/assistant.rs | 71 +++++++++++++++++++++++++++---------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 5102b7408b..0b89969c70 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -198,7 +198,8 @@ { "context": "ContextEditor > Editor", "bindings": { - "cmd-enter": "assistant::Assist" + "cmd-enter": "assistant::Assist", + "escape": "assistant::CancelLastAssist" } }, { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index ef202e7214..0c58f7a479 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,23 +1,24 @@ use crate::{stream_completion, OpenAIRequest, RequestMessage, Role}; -use editor::{Editor, ExcerptRange, MultiBuffer}; +use editor::{Editor, MultiBuffer}; use futures::StreamExt; use gpui::{ - actions, elements::*, Action, AppContext, Entity, ModelHandle, Subscription, View, ViewContext, - ViewHandle, WeakViewHandle, WindowContext, + actions, elements::*, Action, AppContext, Entity, ModelHandle, Subscription, Task, View, + ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use language::{language_settings::SoftWrap, Anchor, Buffer}; use std::sync::Arc; -use util::ResultExt; +use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, item::Item, pane, Pane, Workspace, }; -actions!(assistant, [NewContext, Assist]); +actions!(assistant, [NewContext, Assist, CancelLastAssist]); pub fn init(cx: &mut AppContext) { cx.add_action(ContextEditor::assist); + cx.add_action(ContextEditor::cancel_last_assist); } pub enum AssistantPanelEvent { @@ -203,6 +204,13 @@ impl Panel for AssistantPanel { struct ContextEditor { messages: Vec, editor: ViewHandle, + completion_count: usize, + pending_completions: Vec, +} + +struct PendingCompletion { + id: usize, + task: Task>, } impl ContextEditor { @@ -230,7 +238,12 @@ impl ContextEditor { editor }); - Self { messages, editor } + Self { + messages, + editor, + completion_count: 0, + pending_completions: Vec::new(), + } } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { @@ -265,22 +278,42 @@ impl ContextEditor { ); }); }); - cx.spawn(|_, mut cx| async move { - let mut messages = stream.await?; + let task = cx.spawn(|this, mut cx| { + async move { + let mut messages = stream.await?; - while let Some(message) = messages.next().await { - let mut message = message?; - if let Some(choice) = message.choices.pop() { - content.update(&mut cx, |content, cx| { - let text: Arc = choice.delta.content?.into(); - content.edit([(content.len()..content.len(), text)], None, cx); - Some(()) - }); + while let Some(message) = messages.next().await { + let mut message = message?; + if let Some(choice) = message.choices.pop() { + content.update(&mut cx, |content, cx| { + let text: Arc = choice.delta.content?.into(); + content.edit([(content.len()..content.len(), text)], None, cx); + Some(()) + }); + } } + + this.update(&mut cx, |this, _| { + this.pending_completions + .retain(|completion| completion.id != this.completion_count); + }) + .ok(); + + anyhow::Ok(()) } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + .log_err() + }); + + self.pending_completions.push(PendingCompletion { + id: post_inc(&mut self.completion_count), + task, + }); + } + } + + fn cancel_last_assist(&mut self, _: &CancelLastAssist, cx: &mut ViewContext) { + if self.pending_completions.pop().is_none() { + cx.propagate_action(); } } } From 3904971bd80b9351cd7b6c2a5e02e83e19fbcc3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 26 May 2023 15:38:03 -0600 Subject: [PATCH 003/131] Hide assistant gutter --- crates/ai/src/ai.rs | 1 - crates/ai/src/assistant.rs | 8 +++++++- crates/editor/src/editor.rs | 9 +++++++++ crates/editor/src/element.rs | 2 +- crates/theme/src/theme.rs | 6 ++++++ styles/src/styleTree/app.ts | 2 ++ styles/src/styleTree/assistant.ts | 13 +++++++++++++ 7 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 styles/src/styleTree/assistant.ts diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index cd42ee1153..64d8b69628 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -15,7 +15,6 @@ use std::cell::RefCell; use std::fs; use std::rc::Rc; use std::{io, sync::Arc}; -use util::channel::{ReleaseChannel, RELEASE_CHANNEL}; use util::{ResultExt, TryFutureExt}; pub use assistant::AssistantPanel; diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 0c58f7a479..72e4e9fa42 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -235,6 +235,7 @@ impl ContextEditor { let editor = cx.add_view(|cx| { let mut editor = Editor::for_multibuffer(multibuffer, None, cx); editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); + editor.set_show_gutter(false, cx); editor }); @@ -328,7 +329,12 @@ impl View for ContextEditor { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - ChildView::new(&self.editor, cx).into_any() + let theme = &theme::current(cx).assistant; + + ChildView::new(&self.editor, cx) + .contained() + .with_style(theme.container) + .into_any() } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f5d109e15b..31df5069db 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -496,6 +496,7 @@ pub struct Editor { blink_manager: ModelHandle, show_local_selections: bool, mode: EditorMode, + show_gutter: bool, placeholder_text: Option>, highlighted_rows: Option>, #[allow(clippy::type_complexity)] @@ -526,6 +527,7 @@ pub struct Editor { pub struct EditorSnapshot { pub mode: EditorMode, + pub show_gutter: bool, pub display_snapshot: DisplaySnapshot, pub placeholder_text: Option>, is_focused: bool, @@ -1297,6 +1299,7 @@ impl Editor { blink_manager: blink_manager.clone(), show_local_selections: true, mode, + show_gutter: mode == EditorMode::Full, placeholder_text: None, highlighted_rows: None, background_highlights: Default::default(), @@ -1393,6 +1396,7 @@ impl Editor { pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot { EditorSnapshot { mode: self.mode, + show_gutter: self.show_gutter, display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)), scroll_anchor: self.scroll_manager.anchor(), ongoing_scroll: self.scroll_manager.ongoing_scroll(), @@ -6654,6 +6658,11 @@ impl Editor { cx.notify(); } + pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext) { + self.show_gutter = show_gutter; + cx.notify(); + } + pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext) { if let Some(buffer) = self.buffer().read(cx).as_singleton() { if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 6be0650848..5b4da44073 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1899,7 +1899,7 @@ impl Element for EditorElement { let gutter_padding; let gutter_width; let gutter_margin; - if snapshot.mode == EditorMode::Full { + if snapshot.show_gutter { let em_width = style.text.em_width(cx.font_cache()); gutter_padding = (em_width * style.gutter_padding_factor).round(); gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b1c9e9c215..010d6956eb 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -60,6 +60,7 @@ pub struct Theme { pub incoming_call_notification: IncomingCallNotification, pub tooltip: TooltipStyle, pub terminal: TerminalStyle, + pub assistant: AssistantStyle, pub feedback: FeedbackStyle, pub welcome: WelcomeStyle, pub color_scheme: ColorScheme, @@ -967,6 +968,11 @@ pub struct TerminalStyle { pub dim_foreground: Color, } +#[derive(Clone, Deserialize, Default)] +pub struct AssistantStyle { + pub container: ContainerStyle, +} + #[derive(Clone, Deserialize, Default)] pub struct FeedbackStyle { pub submit_button: Interactive, diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 6238e1abe1..a9700a8d99 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -22,6 +22,7 @@ import { ColorScheme } from "../themes/common/colorScheme" import feedback from "./feedback" import welcome from "./welcome" import copilot from "./copilot" +import assistant from "./assistant" export default function app(colorScheme: ColorScheme): Object { return { @@ -50,6 +51,7 @@ export default function app(colorScheme: ColorScheme): Object { simpleMessageNotification: simpleMessageNotification(colorScheme), tooltip: tooltip(colorScheme), terminal: terminal(colorScheme), + assistant: assistant(colorScheme), feedback: feedback(colorScheme), colorScheme: { ...colorScheme, diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts new file mode 100644 index 0000000000..810831285e --- /dev/null +++ b/styles/src/styleTree/assistant.ts @@ -0,0 +1,13 @@ +import { ColorScheme } from "../themes/common/colorScheme" +import editor from "./editor" + +export default function assistant(colorScheme: ColorScheme) { + return { + container: { + background: editor(colorScheme).background, + padding: { + left: 10, + } + } + } +} From ffbfbe422b6a2c3a43013f9d90e30bb430db5650 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 26 May 2023 16:11:58 -0600 Subject: [PATCH 004/131] WIP: Not sure I actually want to rip this out --- crates/ai/src/ai.rs | 241 +----------------- crates/ai/src/assistant.rs | 128 +++++++--- crates/zed/src/languages/markdown/config.toml | 2 +- 3 files changed, 101 insertions(+), 270 deletions(-) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 64d8b69628..89999f26f3 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,21 +1,7 @@ mod assistant; -use anyhow::{anyhow, Result}; -use assets::Assets; -use collections::HashMap; -use editor::Editor; -use futures::AsyncBufReadExt; -use futures::{io::BufReader, AsyncReadExt, Stream, StreamExt}; -use gpui::executor::Background; -use gpui::{actions, AppContext, Task, ViewContext}; -use isahc::prelude::*; -use isahc::{http::StatusCode, Request}; +use gpui::{actions, AppContext}; use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::fs; -use std::rc::Rc; -use std::{io, sync::Arc}; -use util::{ResultExt, TryFutureExt}; pub use assistant::AssistantPanel; @@ -89,230 +75,5 @@ struct OpenAIChoice { } pub fn init(cx: &mut AppContext) { - // if *RELEASE_CHANNEL == ReleaseChannel::Stable { - // return; - // } - assistant::init(cx); - - // let assistant = Rc::new(Assistant::default()); - // cx.add_action({ - // let assistant = assistant.clone(); - // move |editor: &mut Editor, _: &Assist, cx: &mut ViewContext| { - // assistant.assist(editor, cx).log_err(); - // } - // }); - // cx.capture_action({ - // let assistant = assistant.clone(); - // move |_: &mut Editor, _: &editor::Cancel, cx: &mut ViewContext| { - // if !assistant.cancel_last_assist(cx.view_id()) { - // cx.propagate_action(); - // } - // } - // }); -} - -type CompletionId = usize; - -#[derive(Default)] -struct Assistant(RefCell); - -#[derive(Default)] -struct AssistantState { - assist_stacks: HashMap>)>>, - next_completion_id: CompletionId, -} - -impl Assistant { - fn assist(self: &Rc, editor: &mut Editor, cx: &mut ViewContext) -> Result<()> { - let api_key = std::env::var("OPENAI_API_KEY")?; - - let selections = editor.selections.all(cx); - let (user_message, insertion_site) = editor.buffer().update(cx, |buffer, cx| { - // Insert markers around selected text as described in the system prompt above. - let snapshot = buffer.snapshot(cx); - let mut user_message = String::new(); - let mut user_message_suffix = String::new(); - let mut buffer_offset = 0; - for selection in selections { - if !selection.is_empty() { - if user_message_suffix.is_empty() { - user_message_suffix.push_str("\n\n"); - } - user_message_suffix.push_str("[Selected excerpt from above]\n"); - user_message_suffix - .extend(snapshot.text_for_range(selection.start..selection.end)); - user_message_suffix.push_str("\n\n"); - } - - user_message.extend(snapshot.text_for_range(buffer_offset..selection.start)); - user_message.push_str("[SELECTION_START]"); - user_message.extend(snapshot.text_for_range(selection.start..selection.end)); - buffer_offset = selection.end; - user_message.push_str("[SELECTION_END]"); - } - if buffer_offset < snapshot.len() { - user_message.extend(snapshot.text_for_range(buffer_offset..snapshot.len())); - } - user_message.push_str(&user_message_suffix); - - // Ensure the document ends with 4 trailing newlines. - let trailing_newline_count = snapshot - .reversed_chars_at(snapshot.len()) - .take_while(|c| *c == '\n') - .take(4); - let buffer_suffix = "\n".repeat(4 - trailing_newline_count.count()); - buffer.edit([(snapshot.len()..snapshot.len(), buffer_suffix)], None, cx); - - let snapshot = buffer.snapshot(cx); // Take a new snapshot after editing. - let insertion_site = snapshot.anchor_after(snapshot.len() - 2); - - (user_message, insertion_site) - }); - - let this = self.clone(); - let buffer = editor.buffer().clone(); - let executor = cx.background_executor().clone(); - let editor_id = cx.view_id(); - let assist_id = util::post_inc(&mut self.0.borrow_mut().next_completion_id); - let assist_task = cx.spawn(|_, mut cx| { - async move { - // TODO: We should have a get_string method on assets. This is repateated elsewhere. - let content = Assets::get("contexts/system.zmd").unwrap(); - let mut system_message = std::str::from_utf8(content.data.as_ref()) - .unwrap() - .to_string(); - - if let Ok(custom_system_message_path) = - std::env::var("ZED_ASSISTANT_SYSTEM_PROMPT_PATH") - { - system_message.push_str( - "\n\nAlso consider the following user-defined system prompt:\n\n", - ); - // TODO: Replace this with our file system trait object. - system_message.push_str( - &cx.background() - .spawn(async move { fs::read_to_string(custom_system_message_path) }) - .await?, - ); - } - - let stream = stream_completion( - api_key, - executor, - OpenAIRequest { - model: "gpt-4".to_string(), - messages: vec![ - RequestMessage { - role: Role::System, - content: system_message.to_string(), - }, - RequestMessage { - role: Role::User, - content: user_message, - }, - ], - stream: false, - }, - ); - - let mut messages = stream.await?; - while let Some(message) = messages.next().await { - let mut message = message?; - if let Some(choice) = message.choices.pop() { - buffer.update(&mut cx, |buffer, cx| { - let text: Arc = choice.delta.content?.into(); - buffer.edit([(insertion_site.clone()..insertion_site, text)], None, cx); - Some(()) - }); - } - } - - this.0 - .borrow_mut() - .assist_stacks - .get_mut(&editor_id) - .unwrap() - .retain(|(id, _)| *id != assist_id); - - anyhow::Ok(()) - } - .log_err() - }); - - self.0 - .borrow_mut() - .assist_stacks - .entry(cx.view_id()) - .or_default() - .push((dbg!(assist_id), assist_task)); - - Ok(()) - } - - fn cancel_last_assist(self: &Rc, editor_id: usize) -> bool { - self.0 - .borrow_mut() - .assist_stacks - .get_mut(&editor_id) - .and_then(|assists| assists.pop()) - .is_some() - } -} - -async fn stream_completion( - api_key: String, - executor: Arc, - mut request: OpenAIRequest, -) -> Result>> { - request.stream = true; - - let (tx, rx) = futures::channel::mpsc::unbounded::>(); - - let json_data = serde_json::to_string(&request)?; - let mut response = Request::post("https://api.openai.com/v1/chat/completions") - .header("Content-Type", "application/json") - .header("Authorization", format!("Bearer {}", api_key)) - .body(json_data)? - .send_async() - .await?; - - let status = response.status(); - if status == StatusCode::OK { - executor - .spawn(async move { - let mut lines = BufReader::new(response.body_mut()).lines(); - - fn parse_line( - line: Result, - ) -> Result> { - if let Some(data) = line?.strip_prefix("data: ") { - let event = serde_json::from_str(&data)?; - Ok(Some(event)) - } else { - Ok(None) - } - } - - while let Some(line) = lines.next().await { - if let Some(event) = parse_line(line).transpose() { - tx.unbounded_send(event).log_err(); - } - } - - anyhow::Ok(()) - }) - .detach(); - - Ok(rx) - } else { - let mut body = String::new(); - response.body_mut().read_to_string(&mut body).await?; - - Err(anyhow!( - "Failed to connect to OpenAI API: {} {}", - response.status(), - body, - )) - } } diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 72e4e9fa42..e2796f4ecf 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,12 +1,14 @@ -use crate::{stream_completion, OpenAIRequest, RequestMessage, Role}; +use crate::{OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role}; +use anyhow::{anyhow, Result}; use editor::{Editor, MultiBuffer}; -use futures::StreamExt; +use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ - actions, elements::*, Action, AppContext, Entity, ModelHandle, Subscription, Task, View, - ViewContext, ViewHandle, WeakViewHandle, WindowContext, + actions, elements::*, executor::Background, Action, AppContext, Entity, ModelHandle, + Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; +use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Anchor, Buffer}; -use std::sync::Arc; +use std::{io, sync::Arc}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, @@ -17,8 +19,8 @@ use workspace::{ actions!(assistant, [NewContext, Assist, CancelLastAssist]); pub fn init(cx: &mut AppContext) { - cx.add_action(ContextEditor::assist); - cx.add_action(ContextEditor::cancel_last_assist); + cx.add_action(Assistant::assist); + cx.capture_action(Assistant::cancel_last_assist); } pub enum AssistantPanelEvent { @@ -37,9 +39,7 @@ pub struct AssistantPanel { impl AssistantPanel { pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { - let weak_self = cx.weak_handle(); let pane = cx.add_view(|cx| { - let window_id = cx.window_id(); let mut pane = Pane::new( workspace.weak_handle(), workspace.app_state().background_actions, @@ -48,16 +48,15 @@ impl AssistantPanel { ); pane.set_can_split(false, cx); pane.set_can_navigate(false, cx); - pane.on_can_drop(move |_, cx| false); + pane.on_can_drop(move |_, _| false); pane.set_render_tab_bar_buttons(cx, move |pane, cx| { - let this = weak_self.clone(); Flex::row() .with_child(Pane::render_tab_bar_button( 0, "icons/plus_12.svg", Some(("New Context".into(), Some(Box::new(NewContext)))), cx, - move |_, cx| {}, + move |_, _| todo!(), None, )) .with_child(Pane::render_tab_bar_button( @@ -123,7 +122,7 @@ impl View for AssistantPanel { } impl Panel for AssistantPanel { - fn position(&self, cx: &WindowContext) -> DockPosition { + fn position(&self, _: &WindowContext) -> DockPosition { DockPosition::Right } @@ -131,9 +130,11 @@ impl Panel for AssistantPanel { matches!(position, DockPosition::Right) } - fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) {} + fn set_position(&mut self, _: DockPosition, _: &mut ViewContext) { + // TODO! + } - fn size(&self, cx: &WindowContext) -> f32 { + fn size(&self, _: &WindowContext) -> f32 { self.width.unwrap_or(480.) } @@ -164,7 +165,7 @@ impl Panel for AssistantPanel { if let Some(workspace) = this.workspace.upgrade(cx) { workspace.update(cx, |workspace, cx| { let focus = this.pane.read(cx).has_focus(); - let editor = Box::new(cx.add_view(|cx| ContextEditor::new(cx))); + let editor = Box::new(cx.add_view(|cx| Assistant::new(cx))); Pane::add_item(workspace, &this.pane, editor, true, focus, None, cx); }) } @@ -180,7 +181,8 @@ impl Panel for AssistantPanel { ("Assistant Panel".into(), None) } - fn should_change_position_on_event(event: &Self::Event) -> bool { + fn should_change_position_on_event(_: &Self::Event) -> bool { + // TODO! false } @@ -201,7 +203,7 @@ impl Panel for AssistantPanel { } } -struct ContextEditor { +struct Assistant { messages: Vec, editor: ViewHandle, completion_count: usize, @@ -210,10 +212,10 @@ struct ContextEditor { struct PendingCompletion { id: usize, - task: Task>, + _task: Task>, } -impl ContextEditor { +impl Assistant { fn new(cx: &mut ViewContext) -> Self { let messages = vec![Message { role: Role::User, @@ -264,15 +266,26 @@ impl ContextEditor { if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() { let stream = stream_completion(api_key, cx.background_executor().clone(), request); - let content = cx.add_model(|cx| Buffer::new(0, "", cx)); + let response_buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); self.messages.push(Message { role: Role::Assistant, - content: content.clone(), + content: response_buffer.clone(), + }); + let next_request_buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); + self.messages.push(Message { + role: Role::User, + content: next_request_buffer.clone(), }); self.editor.update(cx, |editor, cx| { editor.buffer().update(cx, |multibuffer, cx| { multibuffer.push_excerpts_with_context_lines( - content.clone(), + response_buffer.clone(), + vec![Anchor::MIN..Anchor::MAX], + 0, + cx, + ); + multibuffer.push_excerpts_with_context_lines( + next_request_buffer, vec![Anchor::MIN..Anchor::MAX], 0, cx, @@ -286,7 +299,7 @@ impl ContextEditor { while let Some(message) = messages.next().await { let mut message = message?; if let Some(choice) = message.choices.pop() { - content.update(&mut cx, |content, cx| { + response_buffer.update(&mut cx, |content, cx| { let text: Arc = choice.delta.content?.into(); content.edit([(content.len()..content.len(), text)], None, cx); Some(()) @@ -307,23 +320,23 @@ impl ContextEditor { self.pending_completions.push(PendingCompletion { id: post_inc(&mut self.completion_count), - task, + _task: task, }); } } - fn cancel_last_assist(&mut self, _: &CancelLastAssist, cx: &mut ViewContext) { + fn cancel_last_assist(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { if self.pending_completions.pop().is_none() { cx.propagate_action(); } } } -impl Entity for ContextEditor { +impl Entity for Assistant { type Event = (); } -impl View for ContextEditor { +impl View for Assistant { fn ui_name() -> &'static str { "ContextEditor" } @@ -338,7 +351,7 @@ impl View for ContextEditor { } } -impl Item for ContextEditor { +impl Item for Assistant { fn tab_content( &self, _: Option, @@ -353,3 +366,60 @@ struct Message { role: Role, content: ModelHandle, } + +async fn stream_completion( + api_key: String, + executor: Arc, + mut request: OpenAIRequest, +) -> Result>> { + request.stream = true; + + let (tx, rx) = futures::channel::mpsc::unbounded::>(); + + let json_data = serde_json::to_string(&request)?; + let mut response = Request::post("https://api.openai.com/v1/chat/completions") + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body(json_data)? + .send_async() + .await?; + + let status = response.status(); + if status == StatusCode::OK { + executor + .spawn(async move { + let mut lines = BufReader::new(response.body_mut()).lines(); + + fn parse_line( + line: Result, + ) -> Result> { + if let Some(data) = line?.strip_prefix("data: ") { + let event = serde_json::from_str(&data)?; + Ok(Some(event)) + } else { + Ok(None) + } + } + + while let Some(line) = lines.next().await { + if let Some(event) = parse_line(line).transpose() { + tx.unbounded_send(event).log_err(); + } + } + + anyhow::Ok(()) + }) + .detach(); + + Ok(rx) + } else { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + Err(anyhow!( + "Failed to connect to OpenAI API: {} {}", + response.status(), + body, + )) + } +} diff --git a/crates/zed/src/languages/markdown/config.toml b/crates/zed/src/languages/markdown/config.toml index 55204cc7a5..2fa3ff3cf2 100644 --- a/crates/zed/src/languages/markdown/config.toml +++ b/crates/zed/src/languages/markdown/config.toml @@ -1,5 +1,5 @@ name = "Markdown" -path_suffixes = ["md", "mdx", "zmd"] +path_suffixes = ["md", "mdx"] brackets = [ { start = "{", end = "}", close = true, newline = true }, { start = "[", end = "]", close = true, newline = true }, From 404bebab63d16c92b76c8920585fde8a79cc58fc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 29 May 2023 11:20:24 +0200 Subject: [PATCH 005/131] Set markdown as the assistant's buffer languages --- crates/ai/src/assistant.rs | 241 ++++++++++++++++++++----------------- crates/zed/src/zed.rs | 7 +- 2 files changed, 135 insertions(+), 113 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index e2796f4ecf..1fa9c3cc4f 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -3,11 +3,11 @@ use anyhow::{anyhow, Result}; use editor::{Editor, MultiBuffer}; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ - actions, elements::*, executor::Background, Action, AppContext, Entity, ModelHandle, - Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + actions, elements::*, executor::Background, Action, AppContext, AsyncAppContext, Entity, + ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use isahc::{http::StatusCode, Request, RequestExt}; -use language::{language_settings::SoftWrap, Anchor, Buffer}; +use language::{language_settings::SoftWrap, Anchor, Buffer, Language, LanguageRegistry}; use std::{io, sync::Arc}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ @@ -38,57 +38,70 @@ pub struct AssistantPanel { } impl AssistantPanel { - pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { - let pane = cx.add_view(|cx| { - let mut pane = Pane::new( - workspace.weak_handle(), - workspace.app_state().background_actions, - Default::default(), - cx, - ); - pane.set_can_split(false, cx); - pane.set_can_navigate(false, cx); - pane.on_can_drop(move |_, _| false); - pane.set_render_tab_bar_buttons(cx, move |pane, cx| { - Flex::row() - .with_child(Pane::render_tab_bar_button( - 0, - "icons/plus_12.svg", - Some(("New Context".into(), Some(Box::new(NewContext)))), - cx, - move |_, _| todo!(), - None, - )) - .with_child(Pane::render_tab_bar_button( - 1, - if pane.is_zoomed() { - "icons/minimize_8.svg" - } else { - "icons/maximize_8.svg" - }, - Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), - cx, - move |pane, cx| pane.toggle_zoom(&Default::default(), cx), - None, - )) - .into_any() - }); - let buffer_search_bar = cx.add_view(search::BufferSearchBar::new); - pane.toolbar() - .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); - pane - }); - let subscriptions = vec![ - cx.observe(&pane, |_, _, cx| cx.notify()), - cx.subscribe(&pane, Self::handle_pane_event), - ]; + pub fn load( + workspace: WeakViewHandle, + cx: AsyncAppContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + // TODO: deserialize state. + workspace.update(&mut cx, |workspace, cx| { + cx.add_view(|cx| { + let pane = cx.add_view(|cx| { + let mut pane = Pane::new( + workspace.weak_handle(), + workspace.app_state().background_actions, + Default::default(), + cx, + ); + pane.set_can_split(false, cx); + pane.set_can_navigate(false, cx); + pane.on_can_drop(move |_, _| false); + pane.set_render_tab_bar_buttons(cx, move |pane, cx| { + Flex::row() + .with_child(Pane::render_tab_bar_button( + 0, + "icons/plus_12.svg", + Some(("New Context".into(), Some(Box::new(NewContext)))), + cx, + move |_, _| todo!(), + None, + )) + .with_child(Pane::render_tab_bar_button( + 1, + if pane.is_zoomed() { + "icons/minimize_8.svg" + } else { + "icons/maximize_8.svg" + }, + Some(( + "Toggle Zoom".into(), + Some(Box::new(workspace::ToggleZoom)), + )), + cx, + move |pane, cx| pane.toggle_zoom(&Default::default(), cx), + None, + )) + .into_any() + }); + let buffer_search_bar = cx.add_view(search::BufferSearchBar::new); + pane.toolbar() + .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); + pane + }); + let subscriptions = vec![ + cx.observe(&pane, |_, _, cx| cx.notify()), + cx.subscribe(&pane, Self::handle_pane_event), + ]; - Self { - pane, - workspace: workspace.weak_handle(), - width: None, - _subscriptions: subscriptions, - } + Self { + pane, + workspace: workspace.weak_handle(), + width: None, + _subscriptions: subscriptions, + } + }) + }) + }) } fn handle_pane_event( @@ -161,15 +174,28 @@ impl Panel for AssistantPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { if active && self.pane.read(cx).items_len() == 0 { - cx.defer(|this, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - let focus = this.pane.read(cx).has_focus(); - let editor = Box::new(cx.add_view(|cx| Assistant::new(cx))); - Pane::add_item(workspace, &this.pane, editor, true, focus, None, cx); - }) - } - }); + let workspace = self.workspace.clone(); + let pane = self.pane.clone(); + let focus = self.has_focus(cx); + cx.spawn(|_, mut cx| async move { + let markdown = workspace + .read_with(&cx, |workspace, _| { + workspace + .app_state() + .languages + .language_for_name("Markdown") + })? + .await?; + workspace.update(&mut cx, |workspace, cx| { + let editor = Box::new(cx.add_view(|cx| { + Assistant::new(markdown, workspace.app_state().languages.clone(), cx) + })); + Pane::add_item(workspace, &pane, editor, true, focus, None, cx); + })?; + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } } @@ -208,6 +234,8 @@ struct Assistant { editor: ViewHandle, completion_count: usize, pending_completions: Vec, + markdown: Arc, + language_registry: Arc, } struct PendingCompletion { @@ -216,37 +244,29 @@ struct PendingCompletion { } impl Assistant { - fn new(cx: &mut ViewContext) -> Self { - let messages = vec![Message { - role: Role::User, - content: cx.add_model(|cx| Buffer::new(0, "", cx)), - }]; - - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - for message in &messages { - multibuffer.push_excerpts_with_context_lines( - message.content.clone(), - vec![Anchor::MIN..Anchor::MAX], - 0, - cx, - ); - } - multibuffer - }); + fn new( + markdown: Arc, + language_registry: Arc, + cx: &mut ViewContext, + ) -> Self { let editor = cx.add_view(|cx| { + let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); let mut editor = Editor::for_multibuffer(multibuffer, None, cx); editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); editor.set_show_gutter(false, cx); editor }); - Self { - messages, + let mut this = Self { + messages: Default::default(), editor, completion_count: 0, pending_completions: Vec::new(), - } + markdown, + language_registry, + }; + this.push_message(Role::User, cx); + this } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { @@ -266,32 +286,8 @@ impl Assistant { if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() { let stream = stream_completion(api_key, cx.background_executor().clone(), request); - let response_buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); - self.messages.push(Message { - role: Role::Assistant, - content: response_buffer.clone(), - }); - let next_request_buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); - self.messages.push(Message { - role: Role::User, - content: next_request_buffer.clone(), - }); - self.editor.update(cx, |editor, cx| { - editor.buffer().update(cx, |multibuffer, cx| { - multibuffer.push_excerpts_with_context_lines( - response_buffer.clone(), - vec![Anchor::MIN..Anchor::MAX], - 0, - cx, - ); - multibuffer.push_excerpts_with_context_lines( - next_request_buffer, - vec![Anchor::MIN..Anchor::MAX], - 0, - cx, - ); - }); - }); + let response_buffer = self.push_message(Role::Assistant, cx); + self.push_message(Role::User, cx); let task = cx.spawn(|this, mut cx| { async move { let mut messages = stream.await?; @@ -330,6 +326,33 @@ impl Assistant { cx.propagate_action(); } } + + fn push_message(&mut self, role: Role, cx: &mut ViewContext) -> ModelHandle { + let content = cx.add_model(|cx| { + let mut buffer = Buffer::new(0, "", cx); + buffer.set_language(Some(self.markdown.clone()), cx); + buffer.set_language_registry(self.language_registry.clone()); + buffer + }); + let message = Message { + role, + content: content.clone(), + }; + self.messages.push(message); + + self.editor.update(cx, |editor, cx| { + editor.buffer().update(cx, |buffer, cx| { + buffer.push_excerpts_with_context_lines( + content.clone(), + vec![Anchor::MIN..Anchor::MAX], + 0, + cx, + ) + }); + }); + + content + } } impl Entity for Assistant { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 24b7d6356b..4c3f21467f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -340,7 +340,9 @@ pub fn initialize_workspace( let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()); - let (project_panel, terminal_panel) = futures::try_join!(project_panel, terminal_panel)?; + let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone()); + let (project_panel, terminal_panel, assistant_panel) = + futures::try_join!(project_panel, terminal_panel, assistant_panel)?; workspace_handle.update(&mut cx, |workspace, cx| { let project_panel_position = project_panel.position(cx); workspace.add_panel(project_panel, cx); @@ -359,9 +361,6 @@ pub fn initialize_workspace( } workspace.add_panel(terminal_panel, cx); - - // TODO: deserialize state. - let assistant_panel = cx.add_view(|cx| AssistantPanel::new(workspace, cx)); workspace.add_panel(assistant_panel, cx); })?; Ok(()) From cf75b090d3f0ff633977df7af5426eb1c66549af Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Mon, 29 May 2023 15:45:13 +0200 Subject: [PATCH 006/131] feat: organize themes by folders with LICENSE --- styles/src/themes/andromeda/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => andromeda}/andromeda.ts | 4 ++-- .../{ => atelier-cave}/atelier-cave-dark.ts | 6 +++--- .../{ => atelier-cave}/atelier-cave-light.ts | 6 +++--- .../{ => atelier-dune}/atelier-dune-dark.ts | 6 +++--- .../{ => atelier-dune}/atelier-dune-light.ts | 6 +++--- .../atelier-estuary-dark.ts | 6 +++--- .../atelier-estuary-light.ts | 6 +++--- .../atelier-forest-dark.ts | 6 +++--- .../atelier-forest-light.ts | 6 +++--- .../{ => atelier-heath}/atelier-heath-dark.ts | 6 +++--- .../atelier-heath-light.ts | 6 +++--- .../atelier-lakeside-dark.ts | 6 +++--- .../atelier-lakeside-light.ts | 6 +++--- .../atelier-plateau-dark.ts | 6 +++--- .../atelier-plateau-light.ts | 6 +++--- .../atelier-savanna-dark.ts | 6 +++--- .../atelier-savanna-light.ts | 6 +++--- .../atelier-seaside-dark.ts | 6 +++--- .../atelier-seaside-light.ts | 6 +++--- .../atelier-sulphurpool-dark.ts | 6 +++--- .../atelier-sulphurpool-light.ts | 6 +++--- styles/src/themes/{ => ayu}/ayu-dark.ts | 4 ++-- styles/src/themes/{ => ayu}/ayu-light.ts | 4 ++-- styles/src/themes/{ => ayu}/ayu-mirage.ts | 4 ++-- styles/src/themes/gruvbox/LICENSE | 21 +++++++++++++++++++ .../themes/{ => gruvbox}/gruvbox-common.ts | 4 ++-- .../themes/{ => gruvbox}/gruvbox-dark-hard.ts | 0 .../themes/{ => gruvbox}/gruvbox-dark-soft.ts | 0 .../src/themes/{ => gruvbox}/gruvbox-dark.ts | 0 .../{ => gruvbox}/gruvbox-light-hard.ts | 0 .../{ => gruvbox}/gruvbox-light-soft.ts | 0 .../src/themes/{ => gruvbox}/gruvbox-light.ts | 0 styles/src/themes/one/LICENSE | 21 +++++++++++++++++++ styles/src/themes/{ => one}/one-dark.ts | 6 +++--- styles/src/themes/{ => one}/one-light.ts | 6 +++--- styles/src/themes/rose-pine/LICENSE | 21 +++++++++++++++++++ .../themes/{ => rose-pine}/rose-pine-dawn.ts | 4 ++-- .../themes/{ => rose-pine}/rose-pine-moon.ts | 4 ++-- .../src/themes/{ => rose-pine}/rose-pine.ts | 4 ++-- styles/src/themes/sandcastle/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => sandcastle}/sandcastle.ts | 4 ++-- styles/src/themes/solarized/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => solarized}/solarized.ts | 4 ++-- styles/src/themes/summercamp/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => summercamp}/summercamp.ts | 4 ++-- 46 files changed, 235 insertions(+), 88 deletions(-) create mode 100644 styles/src/themes/andromeda/LICENSE rename styles/src/themes/{ => andromeda}/andromeda.ts (91%) rename styles/src/themes/{ => atelier-cave}/atelier-cave-dark.ts (89%) rename styles/src/themes/{ => atelier-cave}/atelier-cave-light.ts (90%) rename styles/src/themes/{ => atelier-dune}/atelier-dune-dark.ts (89%) rename styles/src/themes/{ => atelier-dune}/atelier-dune-light.ts (90%) rename styles/src/themes/{ => atelier-estuary}/atelier-estuary-dark.ts (89%) rename styles/src/themes/{ => atelier-estuary}/atelier-estuary-light.ts (90%) rename styles/src/themes/{ => atelier-forest}/atelier-forest-dark.ts (89%) rename styles/src/themes/{ => atelier-forest}/atelier-forest-light.ts (90%) rename styles/src/themes/{ => atelier-heath}/atelier-heath-dark.ts (89%) rename styles/src/themes/{ => atelier-heath}/atelier-heath-light.ts (90%) rename styles/src/themes/{ => atelier-lakeside}/atelier-lakeside-dark.ts (90%) rename styles/src/themes/{ => atelier-lakeside}/atelier-lakeside-light.ts (90%) rename styles/src/themes/{ => atelier-plateau}/atelier-plateau-dark.ts (89%) rename styles/src/themes/{ => atelier-plateau}/atelier-plateau-light.ts (90%) rename styles/src/themes/{ => atelier-savanna}/atelier-savanna-dark.ts (89%) rename styles/src/themes/{ => atelier-savanna}/atelier-savanna-light.ts (90%) rename styles/src/themes/{ => atelier-seaside}/atelier-seaside-dark.ts (89%) rename styles/src/themes/{ => atelier-seaside}/atelier-seaside-light.ts (90%) rename styles/src/themes/{ => atelier-sulphurpool}/atelier-sulphurpool-dark.ts (90%) rename styles/src/themes/{ => atelier-sulphurpool}/atelier-sulphurpool-light.ts (90%) rename styles/src/themes/{ => ayu}/ayu-dark.ts (66%) rename styles/src/themes/{ => ayu}/ayu-light.ts (66%) rename styles/src/themes/{ => ayu}/ayu-mirage.ts (66%) create mode 100644 styles/src/themes/gruvbox/LICENSE rename styles/src/themes/{ => gruvbox}/gruvbox-common.ts (98%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark-hard.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark-soft.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light-hard.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light-soft.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light.ts (100%) create mode 100644 styles/src/themes/one/LICENSE rename styles/src/themes/{ => one}/one-dark.ts (93%) rename styles/src/themes/{ => one}/one-light.ts (93%) create mode 100644 styles/src/themes/rose-pine/LICENSE rename styles/src/themes/{ => rose-pine}/rose-pine-dawn.ts (91%) rename styles/src/themes/{ => rose-pine}/rose-pine-moon.ts (91%) rename styles/src/themes/{ => rose-pine}/rose-pine.ts (91%) create mode 100644 styles/src/themes/sandcastle/LICENSE rename styles/src/themes/{ => sandcastle}/sandcastle.ts (91%) create mode 100644 styles/src/themes/solarized/LICENSE rename styles/src/themes/{ => solarized}/solarized.ts (91%) create mode 100644 styles/src/themes/summercamp/LICENSE rename styles/src/themes/{ => summercamp}/summercamp.ts (91%) diff --git a/styles/src/themes/andromeda/LICENSE b/styles/src/themes/andromeda/LICENSE new file mode 100644 index 0000000000..bdd549491f --- /dev/null +++ b/styles/src/themes/andromeda/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts similarity index 91% rename from styles/src/themes/andromeda.ts rename to styles/src/themes/andromeda/andromeda.ts index 7eba7b1481..adfea37d06 100644 --- a/styles/src/themes/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Andromeda" diff --git a/styles/src/themes/atelier-cave-dark.ts b/styles/src/themes/atelier-cave/atelier-cave-dark.ts similarity index 89% rename from styles/src/themes/atelier-cave-dark.ts rename to styles/src/themes/atelier-cave/atelier-cave-dark.ts index a56e22cd92..a61db0e590 100644 --- a/styles/src/themes/atelier-cave-dark.ts +++ b/styles/src/themes/atelier-cave/atelier-cave-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-cave-light.ts b/styles/src/themes/atelier-cave/atelier-cave-light.ts similarity index 90% rename from styles/src/themes/atelier-cave-light.ts rename to styles/src/themes/atelier-cave/atelier-cave-light.ts index 3b180752cf..739fe1320b 100644 --- a/styles/src/themes/atelier-cave-light.ts +++ b/styles/src/themes/atelier-cave/atelier-cave-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune-dark.ts b/styles/src/themes/atelier-dune/atelier-dune-dark.ts similarity index 89% rename from styles/src/themes/atelier-dune-dark.ts rename to styles/src/themes/atelier-dune/atelier-dune-dark.ts index 0ab402a99d..7a3c3fbd07 100644 --- a/styles/src/themes/atelier-dune-dark.ts +++ b/styles/src/themes/atelier-dune/atelier-dune-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune-light.ts b/styles/src/themes/atelier-dune/atelier-dune-light.ts similarity index 90% rename from styles/src/themes/atelier-dune-light.ts rename to styles/src/themes/atelier-dune/atelier-dune-light.ts index e6a09985d5..3ee60164b6 100644 --- a/styles/src/themes/atelier-dune-light.ts +++ b/styles/src/themes/atelier-dune/atelier-dune-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary-dark.ts b/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts similarity index 89% rename from styles/src/themes/atelier-estuary-dark.ts rename to styles/src/themes/atelier-estuary/atelier-estuary-dark.ts index c4ec50c1e0..5ec6df8d35 100644 --- a/styles/src/themes/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary-light.ts b/styles/src/themes/atelier-estuary/atelier-estuary-light.ts similarity index 90% rename from styles/src/themes/atelier-estuary-light.ts rename to styles/src/themes/atelier-estuary/atelier-estuary-light.ts index 6fce0e4483..bc583d41ac 100644 --- a/styles/src/themes/atelier-estuary-light.ts +++ b/styles/src/themes/atelier-estuary/atelier-estuary-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest-dark.ts b/styles/src/themes/atelier-forest/atelier-forest-dark.ts similarity index 89% rename from styles/src/themes/atelier-forest-dark.ts rename to styles/src/themes/atelier-forest/atelier-forest-dark.ts index 7c47c55a83..72f958efdf 100644 --- a/styles/src/themes/atelier-forest-dark.ts +++ b/styles/src/themes/atelier-forest/atelier-forest-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest-light.ts b/styles/src/themes/atelier-forest/atelier-forest-light.ts similarity index 90% rename from styles/src/themes/atelier-forest-light.ts rename to styles/src/themes/atelier-forest/atelier-forest-light.ts index 8ce0616476..463350f822 100644 --- a/styles/src/themes/atelier-forest-light.ts +++ b/styles/src/themes/atelier-forest/atelier-forest-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath-dark.ts b/styles/src/themes/atelier-heath/atelier-heath-dark.ts similarity index 89% rename from styles/src/themes/atelier-heath-dark.ts rename to styles/src/themes/atelier-heath/atelier-heath-dark.ts index 87458ab8f5..010169b022 100644 --- a/styles/src/themes/atelier-heath-dark.ts +++ b/styles/src/themes/atelier-heath/atelier-heath-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath-light.ts b/styles/src/themes/atelier-heath/atelier-heath-light.ts similarity index 90% rename from styles/src/themes/atelier-heath-light.ts rename to styles/src/themes/atelier-heath/atelier-heath-light.ts index 3db3437041..09d39b6d66 100644 --- a/styles/src/themes/atelier-heath-light.ts +++ b/styles/src/themes/atelier-heath/atelier-heath-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside-dark.ts b/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts similarity index 90% rename from styles/src/themes/atelier-lakeside-dark.ts rename to styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts index a8297ef9fd..59728f30f3 100644 --- a/styles/src/themes/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside-light.ts b/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts similarity index 90% rename from styles/src/themes/atelier-lakeside-light.ts rename to styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts index 408fabcaf3..d26c4fe679 100644 --- a/styles/src/themes/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau-dark.ts b/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts similarity index 89% rename from styles/src/themes/atelier-plateau-dark.ts rename to styles/src/themes/atelier-plateau/atelier-plateau-dark.ts index 731cb824e4..8299116c0e 100644 --- a/styles/src/themes/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau-light.ts b/styles/src/themes/atelier-plateau/atelier-plateau-light.ts similarity index 90% rename from styles/src/themes/atelier-plateau-light.ts rename to styles/src/themes/atelier-plateau/atelier-plateau-light.ts index 96f295a695..7fcc4220ac 100644 --- a/styles/src/themes/atelier-plateau-light.ts +++ b/styles/src/themes/atelier-plateau/atelier-plateau-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna-dark.ts b/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts similarity index 89% rename from styles/src/themes/atelier-savanna-dark.ts rename to styles/src/themes/atelier-savanna/atelier-savanna-dark.ts index dfcb4f27cb..5b6f034563 100644 --- a/styles/src/themes/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna-light.ts b/styles/src/themes/atelier-savanna/atelier-savanna-light.ts similarity index 90% rename from styles/src/themes/atelier-savanna-light.ts rename to styles/src/themes/atelier-savanna/atelier-savanna-light.ts index 4bc1389fc9..4989594876 100644 --- a/styles/src/themes/atelier-savanna-light.ts +++ b/styles/src/themes/atelier-savanna/atelier-savanna-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside-dark.ts b/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts similarity index 89% rename from styles/src/themes/atelier-seaside-dark.ts rename to styles/src/themes/atelier-seaside/atelier-seaside-dark.ts index 1326a27786..00631eff51 100644 --- a/styles/src/themes/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside-light.ts b/styles/src/themes/atelier-seaside/atelier-seaside-light.ts similarity index 90% rename from styles/src/themes/atelier-seaside-light.ts rename to styles/src/themes/atelier-seaside/atelier-seaside-light.ts index 6f6823718a..af6af2f027 100644 --- a/styles/src/themes/atelier-seaside-light.ts +++ b/styles/src/themes/atelier-seaside/atelier-seaside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts similarity index 90% rename from styles/src/themes/atelier-sulphurpool-dark.ts rename to styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts index dcfc0d932a..140d02c2e6 100644 --- a/styles/src/themes/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool-light.ts b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts similarity index 90% rename from styles/src/themes/atelier-sulphurpool-light.ts rename to styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts index b2b5f7c328..b6dba4c384 100644 --- a/styles/src/themes/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts similarity index 66% rename from styles/src/themes/ayu-dark.ts rename to styles/src/themes/ayu/ayu-dark.ts index c7e86994fe..eb837231fe 100644 --- a/styles/src/themes/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts similarity index 66% rename from styles/src/themes/ayu-light.ts rename to styles/src/themes/ayu/ayu-light.ts index 9acabf6a39..9c29449db5 100644 --- a/styles/src/themes/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts similarity index 66% rename from styles/src/themes/ayu-mirage.ts rename to styles/src/themes/ayu/ayu-mirage.ts index 2a01512673..52bf549392 100644 --- a/styles/src/themes/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/gruvbox/LICENSE b/styles/src/themes/gruvbox/LICENSE new file mode 100644 index 0000000000..2a92306143 --- /dev/null +++ b/styles/src/themes/gruvbox/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts similarity index 98% rename from styles/src/themes/gruvbox-common.ts rename to styles/src/themes/gruvbox/gruvbox-common.ts index c42362c11c..5958fe62ac 100644 --- a/styles/src/themes/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Gruvbox" diff --git a/styles/src/themes/gruvbox-dark-hard.ts b/styles/src/themes/gruvbox/gruvbox-dark-hard.ts similarity index 100% rename from styles/src/themes/gruvbox-dark-hard.ts rename to styles/src/themes/gruvbox/gruvbox-dark-hard.ts diff --git a/styles/src/themes/gruvbox-dark-soft.ts b/styles/src/themes/gruvbox/gruvbox-dark-soft.ts similarity index 100% rename from styles/src/themes/gruvbox-dark-soft.ts rename to styles/src/themes/gruvbox/gruvbox-dark-soft.ts diff --git a/styles/src/themes/gruvbox-dark.ts b/styles/src/themes/gruvbox/gruvbox-dark.ts similarity index 100% rename from styles/src/themes/gruvbox-dark.ts rename to styles/src/themes/gruvbox/gruvbox-dark.ts diff --git a/styles/src/themes/gruvbox-light-hard.ts b/styles/src/themes/gruvbox/gruvbox-light-hard.ts similarity index 100% rename from styles/src/themes/gruvbox-light-hard.ts rename to styles/src/themes/gruvbox/gruvbox-light-hard.ts diff --git a/styles/src/themes/gruvbox-light-soft.ts b/styles/src/themes/gruvbox/gruvbox-light-soft.ts similarity index 100% rename from styles/src/themes/gruvbox-light-soft.ts rename to styles/src/themes/gruvbox/gruvbox-light-soft.ts diff --git a/styles/src/themes/gruvbox-light.ts b/styles/src/themes/gruvbox/gruvbox-light.ts similarity index 100% rename from styles/src/themes/gruvbox-light.ts rename to styles/src/themes/gruvbox/gruvbox-light.ts diff --git a/styles/src/themes/one/LICENSE b/styles/src/themes/one/LICENSE new file mode 100644 index 0000000000..dc07dc10ad --- /dev/null +++ b/styles/src/themes/one/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/one-dark.ts b/styles/src/themes/one/one-dark.ts similarity index 93% rename from styles/src/themes/one-dark.ts rename to styles/src/themes/one/one-dark.ts index 85417a0e68..71062940a5 100644 --- a/styles/src/themes/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { fontWeights } from "../common" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { fontWeights } from "../../common" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "One Dark" diff --git a/styles/src/themes/one-light.ts b/styles/src/themes/one/one-light.ts similarity index 93% rename from styles/src/themes/one-light.ts rename to styles/src/themes/one/one-light.ts index 7bf21aee17..8ae42e8b1d 100644 --- a/styles/src/themes/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { fontWeights } from "../common" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { fontWeights } from "../../common" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "One Light" diff --git a/styles/src/themes/rose-pine/LICENSE b/styles/src/themes/rose-pine/LICENSE new file mode 100644 index 0000000000..dfd60136f9 --- /dev/null +++ b/styles/src/themes/rose-pine/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Emilia Dunfelt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts similarity index 91% rename from styles/src/themes/rose-pine-dawn.ts rename to styles/src/themes/rose-pine/rose-pine-dawn.ts index 427b05f72b..48da855304 100644 --- a/styles/src/themes/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine Dawn" diff --git a/styles/src/themes/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts similarity index 91% rename from styles/src/themes/rose-pine-moon.ts rename to styles/src/themes/rose-pine/rose-pine-moon.ts index be2f5a8daf..2e13ca4b70 100644 --- a/styles/src/themes/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine Moon" diff --git a/styles/src/themes/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts similarity index 91% rename from styles/src/themes/rose-pine.ts rename to styles/src/themes/rose-pine/rose-pine.ts index 944550f125..33d7d533e6 100644 --- a/styles/src/themes/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine" diff --git a/styles/src/themes/sandcastle/LICENSE b/styles/src/themes/sandcastle/LICENSE new file mode 100644 index 0000000000..c66a06c51b --- /dev/null +++ b/styles/src/themes/sandcastle/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 George Essig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts similarity index 91% rename from styles/src/themes/sandcastle.ts rename to styles/src/themes/sandcastle/sandcastle.ts index 483f01b27a..0b26521256 100644 --- a/styles/src/themes/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Sandcastle" diff --git a/styles/src/themes/solarized/LICENSE b/styles/src/themes/solarized/LICENSE new file mode 100644 index 0000000000..221eee6f15 --- /dev/null +++ b/styles/src/themes/solarized/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Ethan Schoonover + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/solarized.ts b/styles/src/themes/solarized/solarized.ts similarity index 91% rename from styles/src/themes/solarized.ts rename to styles/src/themes/solarized/solarized.ts index 1210c43806..13e3e34460 100644 --- a/styles/src/themes/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta as Metadata } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta as Metadata } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Solarized" diff --git a/styles/src/themes/summercamp/LICENSE b/styles/src/themes/summercamp/LICENSE new file mode 100644 index 0000000000..d7525414ad --- /dev/null +++ b/styles/src/themes/summercamp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Zoe FiriH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts similarity index 91% rename from styles/src/themes/summercamp.ts rename to styles/src/themes/summercamp/summercamp.ts index 7df125e866..41e101af02 100644 --- a/styles/src/themes/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Summercamp" From 52e8bf29285aad456914a4f23b3c714d4d779def Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 29 May 2023 15:57:55 +0200 Subject: [PATCH 007/131] Show custom header for assistant messages --- crates/ai/src/assistant.rs | 173 +++++++++++++------ crates/editor/src/editor.rs | 25 ++- crates/editor/src/element.rs | 267 +++++++++++++++++------------- crates/theme/src/theme.rs | 3 + styles/src/styleTree/assistant.ts | 16 +- 5 files changed, 313 insertions(+), 171 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1fa9c3cc4f..14ac741114 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,13 +1,15 @@ use crate::{OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role}; use anyhow::{anyhow, Result}; -use editor::{Editor, MultiBuffer}; +use collections::HashMap; +use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer}; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ actions, elements::*, executor::Background, Action, AppContext, AsyncAppContext, Entity, - ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + WindowContext, }; use isahc::{http::StatusCode, Request, RequestExt}; -use language::{language_settings::SoftWrap, Anchor, Buffer, Language, LanguageRegistry}; +use language::{language_settings::SoftWrap, Buffer, Language, LanguageRegistry}; use std::{io, sync::Arc}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ @@ -19,8 +21,8 @@ use workspace::{ actions!(assistant, [NewContext, Assist, CancelLastAssist]); pub fn init(cx: &mut AppContext) { - cx.add_action(Assistant::assist); - cx.capture_action(Assistant::cancel_last_assist); + cx.add_action(AssistantEditor::assist); + cx.capture_action(AssistantEditor::cancel_last_assist); } pub enum AssistantPanelEvent { @@ -188,7 +190,7 @@ impl Panel for AssistantPanel { .await?; workspace.update(&mut cx, |workspace, cx| { let editor = Box::new(cx.add_view(|cx| { - Assistant::new(markdown, workspace.app_state().languages.clone(), cx) + AssistantEditor::new(markdown, workspace.app_state().languages.clone(), cx) })); Pane::add_item(workspace, &pane, editor, true, focus, None, cx); })?; @@ -230,38 +232,31 @@ impl Panel for AssistantPanel { } struct Assistant { + buffer: ModelHandle, messages: Vec, - editor: ViewHandle, + messages_by_id: HashMap, completion_count: usize, pending_completions: Vec, markdown: Arc, language_registry: Arc, } -struct PendingCompletion { - id: usize, - _task: Task>, +impl Entity for Assistant { + type Event = (); } impl Assistant { fn new( markdown: Arc, language_registry: Arc, - cx: &mut ViewContext, + cx: &mut ModelContext, ) -> Self { - let editor = cx.add_view(|cx| { - let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); - let mut editor = Editor::for_multibuffer(multibuffer, None, cx); - editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); - editor.set_show_gutter(false, cx); - editor - }); - let mut this = Self { + buffer: cx.add_model(|_| MultiBuffer::new(0)), messages: Default::default(), - editor, - completion_count: 0, - pending_completions: Vec::new(), + messages_by_id: Default::default(), + completion_count: Default::default(), + pending_completions: Default::default(), markdown, language_registry, }; @@ -269,7 +264,7 @@ impl Assistant { this } - fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { + fn assist(&mut self, cx: &mut ModelContext) { let messages = self .messages .iter() @@ -285,8 +280,8 @@ impl Assistant { }; if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() { - let stream = stream_completion(api_key, cx.background_executor().clone(), request); - let response_buffer = self.push_message(Role::Assistant, cx); + let stream = stream_completion(api_key, cx.background().clone(), request); + let response = self.push_message(Role::Assistant, cx); self.push_message(Role::User, cx); let task = cx.spawn(|this, mut cx| { async move { @@ -295,7 +290,7 @@ impl Assistant { while let Some(message) = messages.next().await { let mut message = message?; if let Some(choice) = message.choices.pop() { - response_buffer.update(&mut cx, |content, cx| { + response.content.update(&mut cx, |content, cx| { let text: Arc = choice.delta.content?.into(); content.edit([(content.len()..content.len(), text)], None, cx); Some(()) @@ -306,8 +301,7 @@ impl Assistant { this.update(&mut cx, |this, _| { this.pending_completions .retain(|completion| completion.id != this.completion_count); - }) - .ok(); + }); anyhow::Ok(()) } @@ -321,45 +315,123 @@ impl Assistant { } } - fn cancel_last_assist(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { - if self.pending_completions.pop().is_none() { - cx.propagate_action(); - } + fn cancel_last_assist(&mut self) -> bool { + self.pending_completions.pop().is_some() } - fn push_message(&mut self, role: Role, cx: &mut ViewContext) -> ModelHandle { + fn push_message(&mut self, role: Role, cx: &mut ModelContext) -> Message { let content = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx); buffer.set_language(Some(self.markdown.clone()), cx); buffer.set_language_registry(self.language_registry.clone()); buffer }); + let excerpt_id = self.buffer.update(cx, |buffer, cx| { + buffer + .push_excerpts( + content.clone(), + vec![ExcerptRange { + context: 0..0, + primary: None, + }], + cx, + ) + .pop() + .unwrap() + }); + let message = Message { role, content: content.clone(), }; - self.messages.push(message); - - self.editor.update(cx, |editor, cx| { - editor.buffer().update(cx, |buffer, cx| { - buffer.push_excerpts_with_context_lines( - content.clone(), - vec![Anchor::MIN..Anchor::MAX], - 0, - cx, - ) - }); - }); - - content + self.messages.push(message.clone()); + self.messages_by_id.insert(excerpt_id, message.clone()); + message } } -impl Entity for Assistant { +struct PendingCompletion { + id: usize, + _task: Task>, +} + +struct AssistantEditor { + assistant: ModelHandle, + editor: ViewHandle, +} + +impl AssistantEditor { + fn new( + markdown: Arc, + language_registry: Arc, + cx: &mut ViewContext, + ) -> Self { + let assistant = cx.add_model(|cx| Assistant::new(markdown, language_registry, cx)); + let editor = cx.add_view(|cx| { + let mut editor = Editor::for_multibuffer(assistant.read(cx).buffer.clone(), None, cx); + editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); + editor.set_show_gutter(false, cx); + editor.set_render_excerpt_header( + { + let assistant = assistant.clone(); + move |editor, params: editor::RenderExcerptHeaderParams, cx| { + let style = &theme::current(cx).assistant; + if let Some(message) = assistant.read(cx).messages_by_id.get(¶ms.id) { + let sender = match message.role { + Role::User => Label::new("You", style.user_sender.text.clone()) + .contained() + .with_style(style.user_sender.container), + Role::Assistant => { + Label::new("Assistant", style.assistant_sender.text.clone()) + .contained() + .with_style(style.assistant_sender.container) + } + Role::System => { + Label::new("System", style.assistant_sender.text.clone()) + .contained() + .with_style(style.assistant_sender.container) + } + }; + + Flex::row() + .with_child(sender) + .aligned() + .left() + .contained() + .with_style(style.header) + .into_any() + } else { + Empty::new().into_any() + } + } + }, + cx, + ); + editor + }); + Self { assistant, editor } + } + + fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { + self.assistant + .update(cx, |assistant, cx| assistant.assist(cx)); + } + + fn cancel_last_assist(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { + if !self + .assistant + .update(cx, |assistant, _| assistant.cancel_last_assist()) + { + cx.propagate_action(); + } + } +} + +impl Entity for AssistantEditor { type Event = (); } -impl View for Assistant { +impl View for AssistantEditor { fn ui_name() -> &'static str { "ContextEditor" } @@ -374,7 +446,7 @@ impl View for Assistant { } } -impl Item for Assistant { +impl Item for AssistantEditor { fn tab_content( &self, _: Option, @@ -385,6 +457,7 @@ impl Item for Assistant { } } +#[derive(Clone)] struct Message { role: Role, content: ModelHandle, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 31df5069db..a1e55dc036 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -46,7 +46,8 @@ use gpui::{ platform::{CursorStyle, MouseButton}, serde_json::{self, json}, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, - ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + LayoutContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -498,6 +499,7 @@ pub struct Editor { mode: EditorMode, show_gutter: bool, placeholder_text: Option>, + render_excerpt_header: Option, highlighted_rows: Option>, #[allow(clippy::type_complexity)] background_highlights: BTreeMap Color, Vec>)>, @@ -1301,6 +1303,7 @@ impl Editor { mode, show_gutter: mode == EditorMode::Full, placeholder_text: None, + render_excerpt_header: None, highlighted_rows: None, background_highlights: Default::default(), nav_history: None, @@ -6663,6 +6666,20 @@ impl Editor { cx.notify(); } + pub fn set_render_excerpt_header( + &mut self, + render_excerpt_header: impl 'static + + Fn( + &mut Editor, + RenderExcerptHeaderParams, + &mut LayoutContext, + ) -> AnyElement, + cx: &mut ViewContext, + ) { + self.render_excerpt_header = Some(Arc::new(render_excerpt_header)); + cx.notify(); + } + pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext) { if let Some(buffer) = self.buffer().read(cx).as_singleton() { if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) { @@ -7308,8 +7325,12 @@ impl View for Editor { }); } + let mut editor = EditorElement::new(style.clone()); + if let Some(render_excerpt_header) = self.render_excerpt_header.clone() { + editor = editor.with_render_excerpt_header(render_excerpt_header); + } Stack::new() - .with_child(EditorElement::new(style.clone())) + .with_child(editor) .with_child(ChildView::new(&self.mouse_context_menu, cx)) .into_any() } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5b4da44073..a028c7ca1c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -91,18 +91,41 @@ impl SelectionLayout { } } -#[derive(Clone)] +pub struct RenderExcerptHeaderParams<'a> { + pub id: crate::ExcerptId, + pub buffer: &'a language::BufferSnapshot, + pub range: &'a crate::ExcerptRange, + pub starts_new_buffer: bool, + pub gutter_padding: f32, + pub editor_style: &'a EditorStyle, +} + +pub type RenderExcerptHeader = Arc< + dyn Fn( + &mut Editor, + RenderExcerptHeaderParams, + &mut LayoutContext, + ) -> AnyElement, +>; + pub struct EditorElement { style: Arc, + render_excerpt_header: RenderExcerptHeader, } impl EditorElement { pub fn new(style: EditorStyle) -> Self { Self { style: Arc::new(style), + render_excerpt_header: Arc::new(render_excerpt_header), } } + pub fn with_render_excerpt_header(mut self, render: RenderExcerptHeader) -> Self { + self.render_excerpt_header = render; + self + } + fn attach_mouse_handlers( scene: &mut SceneBuilder, position_map: &Arc, @@ -1465,11 +1488,9 @@ impl EditorElement { line_height: f32, style: &EditorStyle, line_layouts: &[LineWithInvisibles], - include_root: bool, editor: &mut Editor, cx: &mut LayoutContext, ) -> (f32, Vec) { - let tooltip_style = theme::current(cx).tooltip.clone(); let scroll_x = snapshot.scroll_anchor.offset.x(); let (fixed_blocks, non_fixed_blocks) = snapshot .blocks_in_range(rows.clone()) @@ -1510,112 +1531,18 @@ impl EditorElement { range, starts_new_buffer, .. - } => { - let id = *id; - let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { - let jump_path = ProjectPath { - worktree_id: file.worktree_id(cx), - path: file.path.clone(), - }; - let jump_anchor = range - .primary - .as_ref() - .map_or(range.context.start, |primary| primary.start); - let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); - - enum JumpIcon {} - MouseEventHandler::::new(id.into(), cx, |state, _| { - let style = style.jump_icon.style_for(state, false); - Svg::new("icons/arrow_up_right_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, editor, cx| { - if let Some(workspace) = editor - .workspace - .as_ref() - .and_then(|(workspace, _)| workspace.upgrade(cx)) - { - workspace.update(cx, |workspace, cx| { - Editor::jump( - workspace, - jump_path.clone(), - jump_position, - jump_anchor, - cx, - ); - }); - } - }) - .with_tooltip::( - id.into(), - "Jump to Buffer".to_string(), - Some(Box::new(crate::OpenExcerpts)), - tooltip_style.clone(), - cx, - ) - .aligned() - .flex_float() - }); - - if *starts_new_buffer { - let style = &self.style.diagnostic_path_header; - let font_size = - (style.text_scale_factor * self.style.text.font_size).round(); - - let path = buffer.resolve_file_path(cx, include_root); - let mut filename = None; - let mut parent_path = None; - // Can't use .and_then() because `.file_name()` and `.parent()` return references :( - if let Some(path) = path { - filename = path.file_name().map(|f| f.to_string_lossy().to_string()); - parent_path = - path.parent().map(|p| p.to_string_lossy().to_string() + "/"); - } - - Flex::row() - .with_child( - Label::new( - filename.unwrap_or_else(|| "untitled".to_string()), - style.filename.text.clone().with_font_size(font_size), - ) - .contained() - .with_style(style.filename.container) - .aligned(), - ) - .with_children(parent_path.map(|path| { - Label::new(path, style.path.text.clone().with_font_size(font_size)) - .contained() - .with_style(style.path.container) - .aligned() - })) - .with_children(jump_icon) - .contained() - .with_style(style.container) - .with_padding_left(gutter_padding) - .with_padding_right(gutter_padding) - .expanded() - .into_any_named("path header block") - } else { - let text_style = self.style.text.clone(); - Flex::row() - .with_child(Label::new("⋯", text_style)) - .with_children(jump_icon) - .contained() - .with_padding_left(gutter_padding) - .with_padding_right(gutter_padding) - .expanded() - .into_any_named("collapsed context") - } - } + } => (self.render_excerpt_header)( + editor, + RenderExcerptHeaderParams { + id: *id, + buffer, + range, + starts_new_buffer: *starts_new_buffer, + gutter_padding, + editor_style: style, + }, + cx, + ), }; element.layout( @@ -2080,12 +2007,6 @@ impl Element for EditorElement { ShowScrollbar::Never => false, }; - let include_root = editor - .project - .as_ref() - .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) - .unwrap_or_default(); - let fold_ranges: Vec<(BufferRow, Range, Color)> = fold_ranges .into_iter() .map(|(id, fold)| { @@ -2144,7 +2065,6 @@ impl Element for EditorElement { line_height, &style, &line_layouts, - include_root, editor, cx, ); @@ -2759,6 +2679,121 @@ impl HighlightedRange { } } +fn render_excerpt_header( + editor: &mut Editor, + RenderExcerptHeaderParams { + id, + buffer, + range, + starts_new_buffer, + gutter_padding, + editor_style, + }: RenderExcerptHeaderParams, + cx: &mut LayoutContext, +) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + let include_root = editor + .project + .as_ref() + .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) + .unwrap_or_default(); + let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { + let jump_path = ProjectPath { + worktree_id: file.worktree_id(cx), + path: file.path.clone(), + }; + let jump_anchor = range + .primary + .as_ref() + .map_or(range.context.start, |primary| primary.start); + let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); + + enum JumpIcon {} + MouseEventHandler::::new(id.into(), cx, |state, _| { + let style = editor_style.jump_icon.style_for(state, false); + Svg::new("icons/arrow_up_right_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, editor, cx| { + if let Some(workspace) = editor + .workspace + .as_ref() + .and_then(|(workspace, _)| workspace.upgrade(cx)) + { + workspace.update(cx, |workspace, cx| { + Editor::jump(workspace, jump_path.clone(), jump_position, jump_anchor, cx); + }); + } + }) + .with_tooltip::( + id.into(), + "Jump to Buffer".to_string(), + Some(Box::new(crate::OpenExcerpts)), + tooltip_style.clone(), + cx, + ) + .aligned() + .flex_float() + }); + + if starts_new_buffer { + let style = &editor_style.diagnostic_path_header; + let font_size = (style.text_scale_factor * editor_style.text.font_size).round(); + + let path = buffer.resolve_file_path(cx, include_root); + let mut filename = None; + let mut parent_path = None; + // Can't use .and_then() because `.file_name()` and `.parent()` return references :( + if let Some(path) = path { + filename = path.file_name().map(|f| f.to_string_lossy().to_string()); + parent_path = path.parent().map(|p| p.to_string_lossy().to_string() + "/"); + } + + Flex::row() + .with_child( + Label::new( + filename.unwrap_or_else(|| "untitled".to_string()), + style.filename.text.clone().with_font_size(font_size), + ) + .contained() + .with_style(style.filename.container) + .aligned(), + ) + .with_children(parent_path.map(|path| { + Label::new(path, style.path.text.clone().with_font_size(font_size)) + .contained() + .with_style(style.path.container) + .aligned() + })) + .with_children(jump_icon) + .contained() + .with_style(style.container) + .with_padding_left(gutter_padding) + .with_padding_right(gutter_padding) + .expanded() + .into_any_named("path header block") + } else { + let text_style = editor_style.text.clone(); + Flex::row() + .with_child(Label::new("⋯", text_style)) + .with_children(jump_icon) + .contained() + .with_padding_left(gutter_padding) + .with_padding_right(gutter_padding) + .expanded() + .into_any_named("collapsed context") + } +} + fn position_to_display_point( position: Vector2F, text_bounds: RectF, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 010d6956eb..0056230766 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -971,6 +971,9 @@ pub struct TerminalStyle { #[derive(Clone, Deserialize, Default)] pub struct AssistantStyle { pub container: ContainerStyle, + pub header: ContainerStyle, + pub user_sender: ContainedText, + pub assistant_sender: ContainedText, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 810831285e..ea02c3b383 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -1,13 +1,23 @@ import { ColorScheme } from "../themes/common/colorScheme" +import { text, border } from "./components" import editor from "./editor" export default function assistant(colorScheme: ColorScheme) { + const layer = colorScheme.highest; return { container: { background: editor(colorScheme).background, - padding: { - left: 10, - } + padding: { left: 12 } + }, + header: { + border: border(layer, "default", { bottom: true, top: true }), + margin: { bottom: 6, top: 6 } + }, + user_sender: { + ...text(layer, "sans", "default", { size: "sm", weight: "bold" }), + }, + assistant_sender: { + ...text(layer, "sans", "accent", { size: "sm", weight: "bold" }), } } } From 890c42a75a4fbe5a83f8fef399befc9de99c6045 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 29 May 2023 16:23:16 +0200 Subject: [PATCH 008/131] Show time in assistant messages --- Cargo.lock | 1 + crates/ai/Cargo.toml | 5 +++-- crates/ai/src/assistant.rs | 14 +++++++++++++- crates/theme/src/theme.rs | 1 + styles/src/styleTree/assistant.ts | 4 ++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27c3a2fdb1..a22db1b35d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ version = "0.1.0" dependencies = [ "anyhow", "assets", + "chrono", "collections", "editor", "futures 0.3.28", diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 14817916f4..861a9e4785 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -19,11 +19,12 @@ theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } -serde.workspace = true -serde_json.workspace = true anyhow.workspace = true +chrono = "0.4" futures.workspace = true isahc.workspace = true +serde.workspace = true +serde_json.workspace = true [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 14ac741114..c6c5d4d009 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,5 +1,6 @@ use crate::{OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role}; use anyhow::{anyhow, Result}; +use chrono::{DateTime, Local}; use collections::HashMap; use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer}; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; @@ -343,6 +344,7 @@ impl Assistant { let message = Message { role, content: content.clone(), + sent_at: Local::now(), }; self.messages.push(message.clone()); self.messages_by_id.insert(excerpt_id, message.clone()); @@ -394,7 +396,16 @@ impl AssistantEditor { }; Flex::row() - .with_child(sender) + .with_child(sender.aligned()) + .with_child( + Label::new( + message.sent_at.format("%I:%M%P").to_string(), + style.sent_at.text.clone(), + ) + .contained() + .with_style(style.sent_at.container) + .aligned(), + ) .aligned() .left() .contained() @@ -461,6 +472,7 @@ impl Item for AssistantEditor { struct Message { role: Role, content: ModelHandle, + sent_at: DateTime, } async fn stream_completion( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 0056230766..1f99742cfe 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -972,6 +972,7 @@ pub struct TerminalStyle { pub struct AssistantStyle { pub container: ContainerStyle, pub header: ContainerStyle, + pub sent_at: ContainedText, pub user_sender: ContainedText, pub assistant_sender: ContainedText, } diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index ea02c3b383..0ff65a22ae 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -18,6 +18,10 @@ export default function assistant(colorScheme: ColorScheme) { }, assistant_sender: { ...text(layer, "sans", "accent", { size: "sm", weight: "bold" }), + }, + sent_at: { + margin: { top: 2, left: 8 }, + ...text(layer, "sans", "default", { size: "2xs" }), } } } From 89446c7fd41f3733b657e4f0a57438d323770331 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 29 May 2023 13:48:27 -0700 Subject: [PATCH 009/131] Start work on respecting project-specific settings --- crates/copilot/src/copilot.rs | 5 + crates/copilot_button/src/copilot_button.rs | 2 +- crates/editor/src/display_map.rs | 2 +- crates/editor/src/items.rs | 4 + crates/editor/src/multi_buffer.rs | 20 ++- crates/language/src/buffer.rs | 17 ++- crates/language/src/language_settings.rs | 13 +- crates/project/src/lsp_command.rs | 3 +- crates/project/src/project.rs | 130 +++++++++++++++----- crates/project/src/project_tests.rs | 56 +++++++++ crates/project/src/worktree.rs | 45 +++++-- crates/settings/src/settings_file.rs | 13 +- crates/settings/src/settings_store.rs | 91 +++++++++----- crates/terminal_view/src/terminal_view.rs | 5 +- crates/util/src/paths.rs | 1 + crates/zed/src/languages/yaml.rs | 2 +- 16 files changed, 326 insertions(+), 83 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index de9104a684..88b0aebd17 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -787,6 +787,7 @@ impl Copilot { let position = position.to_point_utf16(buffer); let settings = language_settings( buffer.language_at(position).map(|l| l.name()).as_deref(), + buffer.file().map(|f| f.as_ref()), cx, ); let tab_size = settings.tab_size; @@ -1175,6 +1176,10 @@ mod tests { fn to_proto(&self) -> rpc::proto::File { unimplemented!() } + + fn worktree_id(&self) -> usize { + 0 + } } impl language::LocalFile for File { diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 17d27ca41f..686b3d2dfb 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -198,7 +198,7 @@ impl CopilotButton { if let Some(language) = self.language.clone() { let fs = fs.clone(); let language_enabled = - language_settings::language_settings(Some(language.as_ref()), cx) + language_settings::language_settings(Some(language.as_ref()), None, cx) .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 366e47ddc6..f8d6f0bc40 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -277,7 +277,7 @@ impl DisplayMap { .as_singleton() .and_then(|buffer| buffer.read(cx).language()) .map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + language_settings(language_name.as_deref(), None, cx).tab_size } #[cfg(test)] diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 40e7c89cb2..8da746075e 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1231,6 +1231,10 @@ mod tests { unimplemented!() } + fn worktree_id(&self) -> usize { + 0 + } + fn is_deleted(&self) -> bool { unimplemented!() } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 4650dff38f..597feb832b 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1377,8 +1377,14 @@ impl MultiBuffer { point: T, cx: &'a AppContext, ) -> &'a LanguageSettings { - let language = self.language_at(point, cx); - language_settings(language.map(|l| l.name()).as_deref(), cx) + let mut language = None; + let mut file = None; + if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { + let buffer = buffer.read(cx); + language = buffer.language_at(offset).map(|l| l.name()); + file = buffer.file().map(|f| f.as_ref()); + } + language_settings(language.as_deref(), file, cx) } pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { @@ -2785,9 +2791,13 @@ impl MultiBufferSnapshot { point: T, cx: &'a AppContext, ) -> &'a LanguageSettings { - self.point_to_buffer_offset(point) - .map(|(buffer, offset)| buffer.settings_at(offset, cx)) - .unwrap_or_else(|| language_settings(None, cx)) + let mut language = None; + let mut file = None; + if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { + language = buffer.language_at(offset).map(|l| l.name()); + file = buffer.file().map(|f| f.as_ref()); + } + language_settings(language.as_deref(), file, cx) } pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 93b50cf597..ddec085e2a 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -216,6 +216,11 @@ pub trait File: Send + Sync { /// of its worktree, then this method will return the name of the worktree itself. fn file_name<'a>(&'a self, cx: &'a AppContext) -> &'a OsStr; + /// Returns the id of the worktree to which this file belongs. + /// + /// This is needed for looking up project-specific settings. + fn worktree_id(&self) -> usize; + fn is_deleted(&self) -> bool; fn as_any(&self) -> &dyn Any; @@ -1803,7 +1808,11 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings(language_name.as_deref(), cx); + let settings = language_settings( + language_name.as_deref(), + self.file().map(|f| f.as_ref()), + cx, + ); if settings.hard_tabs { IndentSize::tab() } else { @@ -2128,7 +2137,11 @@ impl BufferSnapshot { cx: &'a AppContext, ) -> &'a LanguageSettings { let language = self.language_at(position); - language_settings(language.map(|l| l.name()).as_deref(), cx) + language_settings( + language.map(|l| l.name()).as_deref(), + self.file.as_ref().map(AsRef::as_ref), + cx, + ) } pub fn language_scope_at(&self, position: D) -> Option { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index c98297c036..70aaca03c4 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -1,3 +1,4 @@ +use crate::File; use anyhow::Result; use collections::HashMap; use globset::GlobMatcher; @@ -13,8 +14,16 @@ pub fn init(cx: &mut AppContext) { settings::register::(cx); } -pub fn language_settings<'a>(language: Option<&str>, cx: &'a AppContext) -> &'a LanguageSettings { - settings::get::(cx).language(language) +pub fn language_settings<'a>( + language: Option<&str>, + file: Option<&dyn File>, + cx: &'a AppContext, +) -> &'a LanguageSettings { + settings::get_local::( + file.map(|f| (f.worktree_id(), f.path().as_ref())), + cx, + ) + .language(language) } pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 5ee6443896..0a8b5f8a2e 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1717,7 +1717,8 @@ impl LspCommand for OnTypeFormatting { let tab_size = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + let file = buffer.file().map(|f| f.as_ref()); + language_settings(language_name.as_deref(), file, cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 92210a75a8..c76587ff56 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -71,7 +71,10 @@ use std::{ time::{Duration, Instant, SystemTime}, }; use terminals::Terminals; -use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _}; +use util::{ + debug_panic, defer, merge_json_value_into, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, + ResultExt, TryFutureExt as _, +}; pub use fs::*; pub use worktree::*; @@ -697,12 +700,7 @@ impl Project { .language(Some(&language.name())) .enable_language_server { - let worktree = file.worktree.read(cx); - language_servers_to_start.push(( - worktree.id(), - worktree.as_local().unwrap().abs_path().clone(), - language.clone(), - )); + language_servers_to_start.push((file.worktree.clone(), language.clone())); } } } @@ -732,8 +730,9 @@ impl Project { } // Start all the newly-enabled language servers. - for (worktree_id, worktree_path, language) in language_servers_to_start { - self.start_language_servers(worktree_id, worktree_path, language, cx); + for (worktree, language) in language_servers_to_start { + let worktree_path = worktree.read(cx).abs_path(); + self.start_language_servers(&worktree, worktree_path, language, cx); } if !self.copilot_enabled && Copilot::global(cx).is_some() { @@ -2320,25 +2319,34 @@ impl Project { }); if let Some(file) = File::from_dyn(buffer.read(cx).file()) { - if let Some(worktree) = file.worktree.read(cx).as_local() { - let worktree_id = worktree.id(); - let worktree_abs_path = worktree.abs_path().clone(); - self.start_language_servers(worktree_id, worktree_abs_path, new_language, cx); + let worktree = file.worktree.clone(); + if let Some(tree) = worktree.read(cx).as_local() { + self.start_language_servers(&worktree, tree.abs_path().clone(), new_language, cx); } } } fn start_language_servers( &mut self, - worktree_id: WorktreeId, + worktree: &ModelHandle, worktree_path: Arc, language: Arc, cx: &mut ModelContext, ) { - if !language_settings(Some(&language.name()), cx).enable_language_server { + if !language_settings( + Some(&language.name()), + worktree + .update(cx, |tree, cx| tree.root_file(cx)) + .as_ref() + .map(|f| f as _), + cx, + ) + .enable_language_server + { return; } + let worktree_id = worktree.read(cx).id(); for adapter in language.lsp_adapters() { let key = (worktree_id, adapter.name.clone()); if self.language_server_ids.contains_key(&key) { @@ -2747,23 +2755,22 @@ impl Project { buffers: impl IntoIterator>, cx: &mut ModelContext, ) -> Option<()> { - let language_server_lookup_info: HashSet<(WorktreeId, Arc, Arc)> = buffers + let language_server_lookup_info: HashSet<(ModelHandle, Arc)> = buffers .into_iter() .filter_map(|buffer| { let buffer = buffer.read(cx); let file = File::from_dyn(buffer.file())?; - let worktree = file.worktree.read(cx).as_local()?; let full_path = file.full_path(cx); let language = self .languages .language_for_file(&full_path, Some(buffer.as_rope())) .now_or_never()? .ok()?; - Some((worktree.id(), worktree.abs_path().clone(), language)) + Some((file.worktree.clone(), language)) }) .collect(); - for (worktree_id, worktree_abs_path, language) in language_server_lookup_info { - self.restart_language_servers(worktree_id, worktree_abs_path, language, cx); + for (worktree, language) in language_server_lookup_info { + self.restart_language_servers(worktree, language, cx); } None @@ -2772,11 +2779,13 @@ impl Project { // TODO This will break in the case where the adapter's root paths and worktrees are not equal fn restart_language_servers( &mut self, - worktree_id: WorktreeId, - fallback_path: Arc, + worktree: ModelHandle, language: Arc, cx: &mut ModelContext, ) { + let worktree_id = worktree.read(cx).id(); + let fallback_path = worktree.read(cx).abs_path(); + let mut stops = Vec::new(); for adapter in language.lsp_adapters() { stops.push(self.stop_language_server(worktree_id, adapter.name.clone(), cx)); @@ -2806,7 +2815,7 @@ impl Project { .map(|path_buf| Arc::from(path_buf.as_path())) .unwrap_or(fallback_path); - this.start_language_servers(worktree_id, root_path, language.clone(), cx); + this.start_language_servers(&worktree, root_path, language.clone(), cx); // Lookup new server ids and set them for each of the orphaned worktrees for adapter in language.lsp_adapters() { @@ -3430,7 +3439,12 @@ impl Project { for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).clone() + language_settings( + language_name.as_deref(), + buffer.file().map(|f| f.as_ref()), + cx, + ) + .clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4439,11 +4453,15 @@ impl Project { push_to_history: bool, cx: &mut ModelContext, ) -> Task>> { - let tab_size = buffer.read_with(cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { + let position = position.to_point_utf16(buffer); + let language_name = buffer.language_at(position).map(|l| l.name()); + let file = buffer.file().map(|f| f.as_ref()); + ( + position, + language_settings(language_name.as_deref(), file, cx).tab_size, + ) }); - let position = position.to_point_utf16(buffer.read(cx)); self.request_lsp( buffer.clone(), OnTypeFormatting { @@ -4849,6 +4867,7 @@ impl Project { worktree::Event::UpdatedEntries(changes) => { this.update_local_worktree_buffers(&worktree, changes, cx); this.update_local_worktree_language_servers(&worktree, changes, cx); + this.update_local_worktree_settings(&worktree, changes, cx); } worktree::Event::UpdatedGitRepositories(updated_repos) => { this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx) @@ -5155,6 +5174,61 @@ impl Project { .detach(); } + pub fn update_local_worktree_settings( + &mut self, + worktree: &ModelHandle, + changes: &UpdatedEntriesSet, + cx: &mut ModelContext, + ) { + let worktree_id = worktree.id(); + let worktree = worktree.read(cx).as_local().unwrap(); + + let mut settings_contents = Vec::new(); + for (path, _, change) in changes.iter() { + if path.ends_with(&*LOCAL_SETTINGS_RELATIVE_PATH) { + let settings_dir = Arc::from( + path.ancestors() + .nth(LOCAL_SETTINGS_RELATIVE_PATH.components().count()) + .unwrap(), + ); + let fs = self.fs.clone(); + let removed = *change == PathChange::Removed; + let abs_path = worktree.absolutize(path); + settings_contents.push(async move { + anyhow::Ok(( + settings_dir, + (!removed).then_some(fs.load(&abs_path).await?), + )) + }); + } + } + + if settings_contents.is_empty() { + return; + } + + cx.spawn_weak(move |_, mut cx| async move { + let settings_contents = futures::future::join_all(settings_contents).await; + cx.update(|cx| { + cx.update_global::(|store, cx| { + for entry in settings_contents { + if let Some((directory, file_content)) = entry.log_err() { + store + .set_local_settings( + worktree_id, + directory, + file_content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + } + } + }); + }); + }) + .detach(); + } + pub fn set_active_path(&mut self, entry: Option, cx: &mut ModelContext) { let new_active_entry = entry.and_then(|project_path| { let worktree = self.worktree_for_id(project_path.worktree_id, cx)?; diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 34b63fd5bd..577b22d730 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -63,6 +63,62 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) { }); } +#[gpui::test] +async fn test_managing_project_specific_settings( + deterministic: Arc, + cx: &mut gpui::TestAppContext, +) { + init_test(cx); + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/the-root", + json!({ + ".zed": { + "settings.json": r#"{ "tab_size": 8 }"# + }, + "a": { + "a.rs": "fn a() {\n A\n}" + }, + "b": { + ".zed": { + "settings.json": r#"{ "tab_size": 2 }"# + }, + "b.rs": "fn b() {\n B\n}" + } + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + let worktree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); + + deterministic.run_until_parked(); + cx.read(|cx| { + let tree = worktree.read(cx); + + let settings_a = language_settings( + None, + Some(&File::for_entry( + tree.entry_for_path("a/a.rs").unwrap().clone(), + worktree.clone(), + )), + cx, + ); + let settings_b = language_settings( + None, + Some(&File::for_entry( + tree.entry_for_path("b/b.rs").unwrap().clone(), + worktree.clone(), + )), + cx, + ); + + assert_eq!(settings_a.tab_size.get(), 8); + assert_eq!(settings_b.tab_size.get(), 2); + }); +} + #[gpui::test] async fn test_managing_language_servers( deterministic: Arc, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index dc3c172775..7432eb8d40 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -677,6 +677,18 @@ impl Worktree { Worktree::Remote(worktree) => worktree.abs_path.clone(), } } + + pub fn root_file(&self, cx: &mut ModelContext) -> Option { + let entry = self.entry_for_path("")?; + Some(File { + worktree: cx.handle(), + path: entry.path.clone(), + mtime: entry.mtime, + entry_id: entry.id, + is_local: self.is_local(), + is_deleted: false, + }) + } } impl LocalWorktree { @@ -684,14 +696,6 @@ impl LocalWorktree { path.starts_with(&self.abs_path) } - fn absolutize(&self, path: &Path) -> PathBuf { - if path.file_name().is_some() { - self.abs_path.join(path) - } else { - self.abs_path.to_path_buf() - } - } - pub(crate) fn load_buffer( &mut self, id: u64, @@ -1544,6 +1548,14 @@ impl Snapshot { &self.abs_path } + pub fn absolutize(&self, path: &Path) -> PathBuf { + if path.file_name().is_some() { + self.abs_path.join(path) + } else { + self.abs_path.to_path_buf() + } + } + pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool { self.entries_by_id.get(&entry_id, &()).is_some() } @@ -2383,6 +2395,10 @@ impl language::File for File { .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name)) } + fn worktree_id(&self) -> usize { + self.worktree.id() + } + fn is_deleted(&self) -> bool { self.is_deleted } @@ -2447,6 +2463,17 @@ impl language::LocalFile for File { } impl File { + pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Self { + Self { + worktree, + path: entry.path.clone(), + mtime: entry.mtime, + entry_id: entry.id, + is_local: true, + is_deleted: false, + } + } + pub fn from_proto( proto: rpc::proto::File, worktree: ModelHandle, @@ -2507,7 +2534,7 @@ pub enum EntryKind { File(CharBag), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PathChange { /// A filesystem entry was was created. Added, diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index cca2909da2..4c98dca51a 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -4,7 +4,14 @@ use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{executor::Background, AppContext, AssetSource}; -use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; +use std::{ + borrow::Cow, + io::ErrorKind, + path::{Path, PathBuf}, + str, + sync::Arc, + time::Duration, +}; use util::{paths, ResultExt}; pub fn register(cx: &mut AppContext) { @@ -17,6 +24,10 @@ pub fn get<'a, T: Setting>(cx: &'a AppContext) -> &'a T { cx.global::().get(None) } +pub fn get_local<'a, T: Setting>(location: Option<(usize, &Path)>, cx: &'a AppContext) -> &'a T { + cx.global::().get(location) +} + pub fn default_settings() -> Cow<'static, str> { match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() { Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 71b3cc635f..2560d0b752 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -89,14 +89,14 @@ pub struct SettingsStore { setting_values: HashMap>, default_deserialized_settings: Option, user_deserialized_settings: Option, - local_deserialized_settings: BTreeMap, serde_json::Value>, + local_deserialized_settings: BTreeMap<(usize, Arc), serde_json::Value>, tab_size_callback: Option<(TypeId, Box Option>)>, } #[derive(Debug)] struct SettingValue { global_value: Option, - local_values: Vec<(Arc, T)>, + local_values: Vec<(usize, Arc, T)>, } trait AnySettingValue { @@ -109,9 +109,9 @@ trait AnySettingValue { custom: &[DeserializedSetting], cx: &AppContext, ) -> Result>; - fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; + fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any; fn set_global_value(&mut self, value: Box); - fn set_local_value(&mut self, path: Arc, value: Box); + fn set_local_value(&mut self, root_id: usize, path: Arc, value: Box); fn json_schema( &self, generator: &mut SchemaGenerator, @@ -165,7 +165,7 @@ impl SettingsStore { /// /// Panics if the given setting type has not been registered, or if there is no /// value for this setting. - pub fn get(&self, path: Option<&Path>) -> &T { + pub fn get(&self, path: Option<(usize, &Path)>) -> &T { self.setting_values .get(&TypeId::of::()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) @@ -343,17 +343,19 @@ impl SettingsStore { /// Add or remove a set of local settings via a JSON string. pub fn set_local_settings( &mut self, + root_id: usize, path: Arc, settings_content: Option<&str>, cx: &AppContext, ) -> Result<()> { if let Some(content) = settings_content { self.local_deserialized_settings - .insert(path.clone(), parse_json_with_comments(content)?); + .insert((root_id, path.clone()), parse_json_with_comments(content)?); } else { - self.local_deserialized_settings.remove(&path); + self.local_deserialized_settings + .remove(&(root_id, path.clone())); } - self.recompute_values(Some(&path), cx)?; + self.recompute_values(Some((root_id, &path)), cx)?; Ok(()) } @@ -436,12 +438,12 @@ impl SettingsStore { fn recompute_values( &mut self, - changed_local_path: Option<&Path>, + changed_local_path: Option<(usize, &Path)>, cx: &AppContext, ) -> Result<()> { // Reload the global and local values for every setting. let mut user_settings_stack = Vec::::new(); - let mut paths_stack = Vec::>::new(); + let mut paths_stack = Vec::>::new(); for setting_value in self.setting_values.values_mut() { if let Some(default_settings) = &self.default_deserialized_settings { let default_settings = setting_value.deserialize_setting(default_settings)?; @@ -469,11 +471,11 @@ impl SettingsStore { } // Reload the local values for the setting. - for (path, local_settings) in &self.local_deserialized_settings { + for ((root_id, path), local_settings) in &self.local_deserialized_settings { // Build a stack of all of the local values for that setting. - while let Some(prev_path) = paths_stack.last() { - if let Some(prev_path) = prev_path { - if !path.starts_with(prev_path) { + while let Some(prev_entry) = paths_stack.last() { + if let Some((prev_root_id, prev_path)) = prev_entry { + if root_id != prev_root_id || !path.starts_with(prev_path) { paths_stack.pop(); user_settings_stack.pop(); continue; @@ -485,14 +487,17 @@ impl SettingsStore { if let Some(local_settings) = setting_value.deserialize_setting(&local_settings).log_err() { - paths_stack.push(Some(path.as_ref())); + paths_stack.push(Some((*root_id, path.as_ref()))); user_settings_stack.push(local_settings); // If a local settings file changed, then avoid recomputing local // settings for any path outside of that directory. - if changed_local_path.map_or(false, |changed_local_path| { - !path.starts_with(changed_local_path) - }) { + if changed_local_path.map_or( + false, + |(changed_root_id, changed_local_path)| { + *root_id != changed_root_id || !path.starts_with(changed_local_path) + }, + ) { continue; } @@ -500,7 +505,7 @@ impl SettingsStore { .load_setting(&default_settings, &user_settings_stack, cx) .log_err() { - setting_value.set_local_value(path.clone(), value); + setting_value.set_local_value(*root_id, path.clone(), value); } } } @@ -510,6 +515,24 @@ impl SettingsStore { } } +impl Debug for SettingsStore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SettingsStore") + .field( + "types", + &self + .setting_values + .values() + .map(|value| value.setting_type_name()) + .collect::>(), + ) + .field("default_settings", &self.default_deserialized_settings) + .field("user_settings", &self.user_deserialized_settings) + .field("local_settings", &self.local_deserialized_settings) + .finish_non_exhaustive() + } +} + impl AnySettingValue for SettingValue { fn key(&self) -> Option<&'static str> { T::KEY @@ -546,10 +569,10 @@ impl AnySettingValue for SettingValue { Ok(DeserializedSetting(Box::new(value))) } - fn value_for_path(&self, path: Option<&Path>) -> &dyn Any { - if let Some(path) = path { - for (settings_path, value) in self.local_values.iter().rev() { - if path.starts_with(&settings_path) { + fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any { + if let Some((root_id, path)) = path { + for (settings_root_id, settings_path, value) in self.local_values.iter().rev() { + if root_id == *settings_root_id && path.starts_with(&settings_path) { return value; } } @@ -563,11 +586,14 @@ impl AnySettingValue for SettingValue { self.global_value = Some(*value.downcast().unwrap()); } - fn set_local_value(&mut self, path: Arc, value: Box) { + fn set_local_value(&mut self, root_id: usize, path: Arc, value: Box) { let value = *value.downcast().unwrap(); - match self.local_values.binary_search_by_key(&&path, |e| &e.0) { - Ok(ix) => self.local_values[ix].1 = value, - Err(ix) => self.local_values.insert(ix, (path, value)), + match self + .local_values + .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1)) + { + Ok(ix) => self.local_values[ix].2 = value, + Err(ix) => self.local_values.insert(ix, (root_id, path, value)), } } @@ -884,6 +910,7 @@ mod tests { store .set_local_settings( + 1, Path::new("/root1").into(), Some(r#"{ "user": { "staff": true } }"#), cx, @@ -891,6 +918,7 @@ mod tests { .unwrap(); store .set_local_settings( + 1, Path::new("/root1/subdir").into(), Some(r#"{ "user": { "name": "Jane Doe" } }"#), cx, @@ -899,6 +927,7 @@ mod tests { store .set_local_settings( + 1, Path::new("/root2").into(), Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), cx, @@ -906,7 +935,7 @@ mod tests { .unwrap(); assert_eq!( - store.get::(Some(Path::new("/root1/something"))), + store.get::(Some((1, Path::new("/root1/something")))), &UserSettings { name: "John Doe".to_string(), age: 31, @@ -914,7 +943,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root1/subdir/something"))), + store.get::(Some((1, Path::new("/root1/subdir/something")))), &UserSettings { name: "Jane Doe".to_string(), age: 31, @@ -922,7 +951,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root2/something"))), + store.get::(Some((1, Path::new("/root2/something")))), &UserSettings { name: "John Doe".to_string(), age: 42, @@ -930,7 +959,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root2/something"))), + store.get::(Some((1, Path::new("/root2/something")))), &MultiKeySettings { key1: "a".to_string(), key2: "b".to_string(), diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 767e3bf4db..2842dfa8a4 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -905,7 +905,10 @@ mod tests { cx: &mut TestAppContext, ) -> (ModelHandle, ViewHandle) { let params = cx.update(AppState::test); - cx.update(|cx| theme::init((), cx)); + cx.update(|cx| { + theme::init((), cx); + language::init(cx); + }); let project = Project::test(params.fs.clone(), [], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index f998fc319f..e3397a1557 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -15,6 +15,7 @@ lazy_static::lazy_static! { pub static ref LAST_USERNAME: PathBuf = CONFIG_DIR.join("last-username.txt"); pub static ref LOG: PathBuf = LOGS_DIR.join("Zed.log"); pub static ref OLD_LOG: PathBuf = LOGS_DIR.join("Zed.log.old"); + pub static ref LOCAL_SETTINGS_RELATIVE_PATH: &'static Path = Path::new(".zed/settings.json"); } pub mod legacy { diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index bd5f2b4c02..d66602ee04 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -107,7 +107,7 @@ impl LspAdapter for YamlLspAdapter { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": language_settings(Some("YAML"), cx).tab_size, + "editor.tabSize": language_settings(Some("YAML"), None, cx).tab_size, } })) .boxed(), From ed0fa2404c3cc53fb4e59a21692fcc18bfe1f4b4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 29 May 2023 14:31:54 -0700 Subject: [PATCH 010/131] Use settings JSON schema when editing local settings files --- crates/zed/src/languages/json.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 406d54cc03..3947c62a6d 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -135,7 +135,10 @@ impl LspAdapter for JsonLspAdapter { }, "schemas": [ { - "fileMatch": [schema_file_match(&paths::SETTINGS)], + "fileMatch": [ + schema_file_match(&paths::SETTINGS), + &*paths::LOCAL_SETTINGS_RELATIVE_PATH, + ], "schema": settings_schema, }, { From 2269c19169741fc9a86e9cf9cbec50b4132e7782 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 14:06:19 +0200 Subject: [PATCH 011/131] chore: add LICENSE files --- styles/src/themes/atelier-cave/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-dune/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-estuary/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-forest/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-heath/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-lakeside/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-plateau/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-savanna/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-seaside/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-sulphurpool/LICENSE | 21 +++++++++++++++++++ styles/src/themes/ayu/LICENSE | 21 +++++++++++++++++++ 11 files changed, 231 insertions(+) create mode 100644 styles/src/themes/atelier-cave/LICENSE create mode 100644 styles/src/themes/atelier-dune/LICENSE create mode 100644 styles/src/themes/atelier-estuary/LICENSE create mode 100644 styles/src/themes/atelier-forest/LICENSE create mode 100644 styles/src/themes/atelier-heath/LICENSE create mode 100644 styles/src/themes/atelier-lakeside/LICENSE create mode 100644 styles/src/themes/atelier-plateau/LICENSE create mode 100644 styles/src/themes/atelier-savanna/LICENSE create mode 100644 styles/src/themes/atelier-seaside/LICENSE create mode 100644 styles/src/themes/atelier-sulphurpool/LICENSE create mode 100644 styles/src/themes/ayu/LICENSE diff --git a/styles/src/themes/atelier-cave/LICENSE b/styles/src/themes/atelier-cave/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-cave/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-dune/LICENSE b/styles/src/themes/atelier-dune/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-dune/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-estuary/LICENSE b/styles/src/themes/atelier-estuary/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-estuary/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-forest/LICENSE b/styles/src/themes/atelier-forest/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-forest/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-heath/LICENSE b/styles/src/themes/atelier-heath/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-heath/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-lakeside/LICENSE b/styles/src/themes/atelier-lakeside/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-lakeside/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-plateau/LICENSE b/styles/src/themes/atelier-plateau/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-plateau/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-savanna/LICENSE b/styles/src/themes/atelier-savanna/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-savanna/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-seaside/LICENSE b/styles/src/themes/atelier-seaside/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-seaside/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-sulphurpool/LICENSE b/styles/src/themes/atelier-sulphurpool/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-sulphurpool/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/ayu/LICENSE b/styles/src/themes/ayu/LICENSE new file mode 100644 index 0000000000..6b83ef0582 --- /dev/null +++ b/styles/src/themes/ayu/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Ike Ku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 69e8a166e4e3409583678d880f429c8c72e51b18 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 30 May 2023 15:25:53 +0200 Subject: [PATCH 012/131] Start on `assistant::QuoteSelection` --- assets/keymaps/default.json | 3 +- crates/ai/src/assistant.rs | 85 ++++++++++++++++++++++++++++++- crates/workspace/src/dock.rs | 6 +++ crates/workspace/src/workspace.rs | 10 ++++ 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 0b89969c70..c6964b49e2 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -199,7 +199,8 @@ "context": "ContextEditor > Editor", "bindings": { "cmd-enter": "assistant::Assist", - "escape": "assistant::CancelLastAssist" + "escape": "assistant::CancelLastAssist", + "cmd-?": "assistant::QuoteSelection" } }, { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 23baaef04a..6e76e46c61 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -19,11 +19,15 @@ use workspace::{ pane, Pane, Workspace, }; -actions!(assistant, [NewContext, Assist, CancelLastAssist]); +actions!( + assistant, + [NewContext, Assist, CancelLastAssist, QuoteSelection] +); pub fn init(cx: &mut AppContext) { cx.add_action(AssistantEditor::assist); cx.capture_action(AssistantEditor::cancel_last_assist); + cx.add_action(AssistantEditor::quote_selection); } pub enum AssistantPanelEvent { @@ -136,6 +140,12 @@ impl View for AssistantPanel { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { ChildView::new(&self.pane, cx).into_any() } + + fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(&self.pane); + } + } } impl Panel for AssistantPanel { @@ -361,7 +371,7 @@ impl AssistantEditor { editor.set_render_excerpt_header( { let assistant = assistant.clone(); - move |editor, params: editor::RenderExcerptHeaderParams, cx| { + move |_editor, params: editor::RenderExcerptHeaderParams, cx| { let style = &theme::current(cx).assistant; if let Some(message) = assistant.read(cx).messages_by_id.get(¶ms.id) { let sender = match message.role { @@ -421,6 +431,71 @@ impl AssistantEditor { cx.propagate_action(); } } + + fn quote_selection( + workspace: &mut Workspace, + _: &QuoteSelection, + cx: &mut ViewContext, + ) { + let Some(panel) = workspace.panel::(cx) else { + return; + }; + let Some(editor) = workspace.active_item(cx).and_then(|item| item.downcast::()) else { + return; + }; + + let text = editor.read_with(cx, |editor, cx| { + let range = editor.selections.newest::(cx).range(); + let buffer = editor.buffer().read(cx).snapshot(cx); + let start_language = buffer.language_at(range.start); + let end_language = buffer.language_at(range.end); + let language_name = if start_language == end_language { + start_language.map(|language| language.name()) + } else { + None + }; + let language_name = language_name.as_deref().unwrap_or("").to_lowercase(); + + let selected_text = buffer.text_for_range(range).collect::(); + if selected_text.is_empty() { + None + } else { + Some(if language_name == "markdown" { + selected_text + .lines() + .map(|line| format!("> {}", line)) + .collect::>() + .join("\n") + } else { + format!("```{language_name}\n{selected_text}\n```") + }) + } + }); + + // Activate the panel + if !panel.read(cx).has_focus(cx) { + workspace.toggle_panel_focus::(cx); + } + + if let Some(text) = text { + panel.update(cx, |panel, cx| { + if let Some(assistant) = panel + .pane + .read(cx) + .active_item() + .and_then(|item| item.downcast::()) + .ok_or_else(|| anyhow!("no active context")) + .log_err() + { + assistant.update(cx, |assistant, cx| { + assistant + .editor + .update(cx, |editor, cx| editor.insert(&text, cx)) + }); + } + }); + } + } } impl Entity for AssistantEditor { @@ -440,6 +515,12 @@ impl View for AssistantEditor { .with_style(theme.container) .into_any() } + + fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(&self.editor); + } + } } impl Item for AssistantEditor { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 73d4f79399..49651486db 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -184,6 +184,12 @@ impl Dock { .map_or(false, |panel| panel.has_focus(cx)) } + pub fn panel(&self) -> Option> { + self.panel_entries + .iter() + .find_map(|entry| entry.panel.as_any().clone().downcast()) + } + pub fn panel_index_for_type(&self) -> Option { self.panel_entries .iter() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8afad46ea1..2aa937c9a9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1678,6 +1678,16 @@ impl Workspace { } } + pub fn panel(&self, cx: &WindowContext) -> Option> { + for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { + let dock = dock.read(cx); + if let Some(panel) = dock.panel::() { + return Some(panel); + } + } + None + } + fn zoom_out(&mut self, cx: &mut ViewContext) { for pane in &self.panes { pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); From 0d62e76cd629d721997d0798ca50832dd28a9073 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 15:52:32 +0200 Subject: [PATCH 013/131] refactor: colorSchemes --- styles/src/buildThemes.ts | 2 +- styles/src/colorSchemes.ts | 76 +++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/styles/src/buildThemes.ts b/styles/src/buildThemes.ts index 2a63a407cc..4353765573 100644 --- a/styles/src/buildThemes.ts +++ b/styles/src/buildThemes.ts @@ -1,7 +1,7 @@ import * as fs from "fs" import { tmpdir } from "os" import * as path from "path" -import colorSchemes, { staffColorSchemes } from "./colorSchemes" +import { colorSchemes, staffColorSchemes } from "./colorSchemes" import app from "./styleTree/app" import { ColorScheme } from "./themes/common/colorScheme" import snakeCase from "./utils/snakeCase" diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index 4d2d7f6e02..e8d0687044 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -2,53 +2,63 @@ import fs from "fs" import path from "path" import { ColorScheme, Meta } from "./themes/common/colorScheme" -const colorSchemes: ColorScheme[] = [] -export default colorSchemes +const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) +const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) +const IGNORE_ITEMS = ["staff", "common", "template.ts"] +const ACCEPT_EXTENSION = ".ts" -const schemeMeta: Meta[] = [] -export { schemeMeta } +function getAllTsFiles(directoryPath: string) { + const files = fs.readdirSync(directoryPath) + const fileList: string[] = [] -const staffColorSchemes: ColorScheme[] = [] -export { staffColorSchemes } + for (const file of files) { + if (!IGNORE_ITEMS.includes(file)) { + const filePath = path.join(directoryPath, file) -const experimentalColorSchemes: ColorScheme[] = [] -export { experimentalColorSchemes } - -const themes_directory = path.resolve(`${__dirname}/themes`) - -function for_all_color_schemes_in( - themesPath: string, - callback: (module: any, path: string) => void -) { - for (const fileName of fs.readdirSync(themesPath)) { - if (fileName == "template.ts") continue - const filePath = path.join(themesPath, fileName) - - if (fs.statSync(filePath).isFile()) { - const colorScheme = require(filePath) - callback(colorScheme, path.basename(filePath)) + if (fs.statSync(filePath).isDirectory()) { + fileList.push(...getAllTsFiles(filePath)) + } else if (path.extname(file) === ACCEPT_EXTENSION) { + fileList.push(filePath) + } } } + + return fileList } -function fillColorSchemes(themesPath: string, colorSchemes: ColorScheme[]) { - for_all_color_schemes_in(themesPath, (colorScheme, _path) => { +function getAllColorSchemes(directoryPath: string) { + const files = getAllTsFiles(directoryPath) + return files.map((filePath) => ({ + colorScheme: require(filePath), + filePath: path.basename(filePath), + })) +} + +function getColorSchemes(directoryPath: string) { + const colorSchemes: ColorScheme[] = [] + + for (const { colorScheme } of getAllColorSchemes(directoryPath)) { if (colorScheme.dark) colorSchemes.push(colorScheme.dark) - if (colorScheme.light) colorSchemes.push(colorScheme.light) - }) + else if (colorScheme.light) colorSchemes.push(colorScheme.light) + } + + return colorSchemes } -fillColorSchemes(themes_directory, colorSchemes) -fillColorSchemes(path.resolve(`${themes_directory}/staff`), staffColorSchemes) +function getMeta(directoryPath: string) { + const meta: Meta[] = [] -function fillMeta(themesPath: string, meta: Meta[]) { - for_all_color_schemes_in(themesPath, (colorScheme, path) => { + for (const { colorScheme, filePath } of getAllColorSchemes(directoryPath)) { if (colorScheme.meta) { meta.push(colorScheme.meta) } else { - throw Error(`Public theme ${path} must have a meta field`) + throw Error(`Public theme ${filePath} must have a meta field`) } - }) + } + + return meta } -fillMeta(themes_directory, schemeMeta) +export const colorSchemes = getColorSchemes(THEMES_DIRECTORY) +export const staffColorSchemes = getColorSchemes(STAFF_DIRECTORY) +export const schemeMeta = getMeta(THEMES_DIRECTORY) From ac63043c273f724777a1ba9254ee6d8e6a882977 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 16:52:28 +0200 Subject: [PATCH 014/131] chore: move similar themes to one folder --- styles/src/themes/atelier-dune/LICENSE | 21 ------------------- styles/src/themes/atelier-estuary/LICENSE | 21 ------------------- styles/src/themes/atelier-forest/LICENSE | 21 ------------------- styles/src/themes/atelier-heath/LICENSE | 21 ------------------- styles/src/themes/atelier-lakeside/LICENSE | 21 ------------------- styles/src/themes/atelier-plateau/LICENSE | 21 ------------------- styles/src/themes/atelier-savanna/LICENSE | 21 ------------------- styles/src/themes/atelier-seaside/LICENSE | 21 ------------------- styles/src/themes/atelier-sulphurpool/LICENSE | 21 ------------------- .../themes/{atelier-cave => atelier}/LICENSE | 0 .../atelier-cave-dark.ts | 2 +- .../atelier-cave-light.ts | 2 +- .../atelier-dune-dark.ts | 2 +- .../atelier-dune-light.ts | 2 +- .../atelier-estuary-dark.ts | 2 +- .../atelier-estuary-light.ts | 2 +- .../atelier-forest-dark.ts | 2 +- .../atelier-forest-light.ts | 2 +- .../atelier-heath-dark.ts | 2 +- .../atelier-heath-light.ts | 2 +- .../atelier-lakeside-dark.ts | 2 +- .../atelier-lakeside-light.ts | 2 +- .../atelier-plateau-dark.ts | 2 +- .../atelier-plateau-light.ts | 2 +- .../atelier-savanna-dark.ts | 2 +- .../atelier-savanna-light.ts | 2 +- .../atelier-seaside-dark.ts | 2 +- .../atelier-seaside-light.ts | 2 +- .../atelier-sulphurpool-dark.ts | 2 +- .../atelier-sulphurpool-light.ts | 2 +- 30 files changed, 20 insertions(+), 209 deletions(-) delete mode 100644 styles/src/themes/atelier-dune/LICENSE delete mode 100644 styles/src/themes/atelier-estuary/LICENSE delete mode 100644 styles/src/themes/atelier-forest/LICENSE delete mode 100644 styles/src/themes/atelier-heath/LICENSE delete mode 100644 styles/src/themes/atelier-lakeside/LICENSE delete mode 100644 styles/src/themes/atelier-plateau/LICENSE delete mode 100644 styles/src/themes/atelier-savanna/LICENSE delete mode 100644 styles/src/themes/atelier-seaside/LICENSE delete mode 100644 styles/src/themes/atelier-sulphurpool/LICENSE rename styles/src/themes/{atelier-cave => atelier}/LICENSE (100%) rename styles/src/themes/{atelier-cave => atelier}/atelier-cave-dark.ts (95%) rename styles/src/themes/{atelier-cave => atelier}/atelier-cave-light.ts (95%) rename styles/src/themes/{atelier-dune => atelier}/atelier-dune-dark.ts (95%) rename styles/src/themes/{atelier-dune => atelier}/atelier-dune-light.ts (95%) rename styles/src/themes/{atelier-estuary => atelier}/atelier-estuary-dark.ts (95%) rename styles/src/themes/{atelier-estuary => atelier}/atelier-estuary-light.ts (95%) rename styles/src/themes/{atelier-forest => atelier}/atelier-forest-dark.ts (95%) rename styles/src/themes/{atelier-forest => atelier}/atelier-forest-light.ts (95%) rename styles/src/themes/{atelier-heath => atelier}/atelier-heath-dark.ts (95%) rename styles/src/themes/{atelier-heath => atelier}/atelier-heath-light.ts (95%) rename styles/src/themes/{atelier-lakeside => atelier}/atelier-lakeside-dark.ts (95%) rename styles/src/themes/{atelier-lakeside => atelier}/atelier-lakeside-light.ts (95%) rename styles/src/themes/{atelier-plateau => atelier}/atelier-plateau-dark.ts (95%) rename styles/src/themes/{atelier-plateau => atelier}/atelier-plateau-light.ts (95%) rename styles/src/themes/{atelier-savanna => atelier}/atelier-savanna-dark.ts (95%) rename styles/src/themes/{atelier-savanna => atelier}/atelier-savanna-light.ts (95%) rename styles/src/themes/{atelier-seaside => atelier}/atelier-seaside-dark.ts (95%) rename styles/src/themes/{atelier-seaside => atelier}/atelier-seaside-light.ts (95%) rename styles/src/themes/{atelier-sulphurpool => atelier}/atelier-sulphurpool-dark.ts (95%) rename styles/src/themes/{atelier-sulphurpool => atelier}/atelier-sulphurpool-light.ts (95%) diff --git a/styles/src/themes/atelier-dune/LICENSE b/styles/src/themes/atelier-dune/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-dune/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-estuary/LICENSE b/styles/src/themes/atelier-estuary/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-estuary/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-forest/LICENSE b/styles/src/themes/atelier-forest/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-forest/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-heath/LICENSE b/styles/src/themes/atelier-heath/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-heath/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-lakeside/LICENSE b/styles/src/themes/atelier-lakeside/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-lakeside/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-plateau/LICENSE b/styles/src/themes/atelier-plateau/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-plateau/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-savanna/LICENSE b/styles/src/themes/atelier-savanna/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-savanna/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-seaside/LICENSE b/styles/src/themes/atelier-seaside/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-seaside/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-sulphurpool/LICENSE b/styles/src/themes/atelier-sulphurpool/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-sulphurpool/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-cave/LICENSE b/styles/src/themes/atelier/LICENSE similarity index 100% rename from styles/src/themes/atelier-cave/LICENSE rename to styles/src/themes/atelier/LICENSE diff --git a/styles/src/themes/atelier-cave/atelier-cave-dark.ts b/styles/src/themes/atelier/atelier-cave-dark.ts similarity index 95% rename from styles/src/themes/atelier-cave/atelier-cave-dark.ts rename to styles/src/themes/atelier/atelier-cave-dark.ts index a61db0e590..41ee6a745c 100644 --- a/styles/src/themes/atelier-cave/atelier-cave-dark.ts +++ b/styles/src/themes/atelier/atelier-cave-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-cave/atelier-cave-light.ts b/styles/src/themes/atelier/atelier-cave-light.ts similarity index 95% rename from styles/src/themes/atelier-cave/atelier-cave-light.ts rename to styles/src/themes/atelier/atelier-cave-light.ts index 739fe1320b..9e0473bbfa 100644 --- a/styles/src/themes/atelier-cave/atelier-cave-light.ts +++ b/styles/src/themes/atelier/atelier-cave-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune/atelier-dune-dark.ts b/styles/src/themes/atelier/atelier-dune-dark.ts similarity index 95% rename from styles/src/themes/atelier-dune/atelier-dune-dark.ts rename to styles/src/themes/atelier/atelier-dune-dark.ts index 7a3c3fbd07..c21ee61a60 100644 --- a/styles/src/themes/atelier-dune/atelier-dune-dark.ts +++ b/styles/src/themes/atelier/atelier-dune-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune/atelier-dune-light.ts b/styles/src/themes/atelier/atelier-dune-light.ts similarity index 95% rename from styles/src/themes/atelier-dune/atelier-dune-light.ts rename to styles/src/themes/atelier/atelier-dune-light.ts index 3ee60164b6..956ae72c60 100644 --- a/styles/src/themes/atelier-dune/atelier-dune-light.ts +++ b/styles/src/themes/atelier/atelier-dune-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts b/styles/src/themes/atelier/atelier-estuary-dark.ts similarity index 95% rename from styles/src/themes/atelier-estuary/atelier-estuary-dark.ts rename to styles/src/themes/atelier/atelier-estuary-dark.ts index 5ec6df8d35..b9f1880d68 100644 --- a/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier/atelier-estuary-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary/atelier-estuary-light.ts b/styles/src/themes/atelier/atelier-estuary-light.ts similarity index 95% rename from styles/src/themes/atelier-estuary/atelier-estuary-light.ts rename to styles/src/themes/atelier/atelier-estuary-light.ts index bc583d41ac..a9eeb612ed 100644 --- a/styles/src/themes/atelier-estuary/atelier-estuary-light.ts +++ b/styles/src/themes/atelier/atelier-estuary-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest/atelier-forest-dark.ts b/styles/src/themes/atelier/atelier-forest-dark.ts similarity index 95% rename from styles/src/themes/atelier-forest/atelier-forest-dark.ts rename to styles/src/themes/atelier/atelier-forest-dark.ts index 72f958efdf..352f1ea43e 100644 --- a/styles/src/themes/atelier-forest/atelier-forest-dark.ts +++ b/styles/src/themes/atelier/atelier-forest-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest/atelier-forest-light.ts b/styles/src/themes/atelier/atelier-forest-light.ts similarity index 95% rename from styles/src/themes/atelier-forest/atelier-forest-light.ts rename to styles/src/themes/atelier/atelier-forest-light.ts index 463350f822..1378c9b061 100644 --- a/styles/src/themes/atelier-forest/atelier-forest-light.ts +++ b/styles/src/themes/atelier/atelier-forest-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath/atelier-heath-dark.ts b/styles/src/themes/atelier/atelier-heath-dark.ts similarity index 95% rename from styles/src/themes/atelier-heath/atelier-heath-dark.ts rename to styles/src/themes/atelier/atelier-heath-dark.ts index 010169b022..4c35194422 100644 --- a/styles/src/themes/atelier-heath/atelier-heath-dark.ts +++ b/styles/src/themes/atelier/atelier-heath-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath/atelier-heath-light.ts b/styles/src/themes/atelier/atelier-heath-light.ts similarity index 95% rename from styles/src/themes/atelier-heath/atelier-heath-light.ts rename to styles/src/themes/atelier/atelier-heath-light.ts index 09d39b6d66..51b9ef9398 100644 --- a/styles/src/themes/atelier-heath/atelier-heath-light.ts +++ b/styles/src/themes/atelier/atelier-heath-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts b/styles/src/themes/atelier/atelier-lakeside-dark.ts similarity index 95% rename from styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts rename to styles/src/themes/atelier/atelier-lakeside-dark.ts index 59728f30f3..ece9179860 100644 --- a/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier/atelier-lakeside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts b/styles/src/themes/atelier/atelier-lakeside-light.ts similarity index 95% rename from styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts rename to styles/src/themes/atelier/atelier-lakeside-light.ts index d26c4fe679..fd265d10c8 100644 --- a/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier/atelier-lakeside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts b/styles/src/themes/atelier/atelier-plateau-dark.ts similarity index 95% rename from styles/src/themes/atelier-plateau/atelier-plateau-dark.ts rename to styles/src/themes/atelier/atelier-plateau-dark.ts index 8299116c0e..ef64782bda 100644 --- a/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier/atelier-plateau-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau/atelier-plateau-light.ts b/styles/src/themes/atelier/atelier-plateau-light.ts similarity index 95% rename from styles/src/themes/atelier-plateau/atelier-plateau-light.ts rename to styles/src/themes/atelier/atelier-plateau-light.ts index 7fcc4220ac..9fe51f5b79 100644 --- a/styles/src/themes/atelier-plateau/atelier-plateau-light.ts +++ b/styles/src/themes/atelier/atelier-plateau-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts b/styles/src/themes/atelier/atelier-savanna-dark.ts similarity index 95% rename from styles/src/themes/atelier-savanna/atelier-savanna-dark.ts rename to styles/src/themes/atelier/atelier-savanna-dark.ts index 5b6f034563..36de9b817f 100644 --- a/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier/atelier-savanna-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna/atelier-savanna-light.ts b/styles/src/themes/atelier/atelier-savanna-light.ts similarity index 95% rename from styles/src/themes/atelier-savanna/atelier-savanna-light.ts rename to styles/src/themes/atelier/atelier-savanna-light.ts index 4989594876..d5d9cb369d 100644 --- a/styles/src/themes/atelier-savanna/atelier-savanna-light.ts +++ b/styles/src/themes/atelier/atelier-savanna-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts b/styles/src/themes/atelier/atelier-seaside-dark.ts similarity index 95% rename from styles/src/themes/atelier-seaside/atelier-seaside-dark.ts rename to styles/src/themes/atelier/atelier-seaside-dark.ts index 00631eff51..f7c49ad71e 100644 --- a/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier/atelier-seaside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside/atelier-seaside-light.ts b/styles/src/themes/atelier/atelier-seaside-light.ts similarity index 95% rename from styles/src/themes/atelier-seaside/atelier-seaside-light.ts rename to styles/src/themes/atelier/atelier-seaside-light.ts index af6af2f027..1cf6461446 100644 --- a/styles/src/themes/atelier-seaside/atelier-seaside-light.ts +++ b/styles/src/themes/atelier/atelier-seaside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts similarity index 95% rename from styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts rename to styles/src/themes/atelier/atelier-sulphurpool-dark.ts index 140d02c2e6..b4a4e2a651 100644 --- a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts b/styles/src/themes/atelier/atelier-sulphurpool-light.ts similarity index 95% rename from styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts rename to styles/src/themes/atelier/atelier-sulphurpool-light.ts index b6dba4c384..046adbdf43 100644 --- a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { From 05d1dd6b11386002680d8d0df67e975c56517ce2 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 16:53:12 +0200 Subject: [PATCH 015/131] refactor: move common to theme folder --- styles/src/colorSchemes.ts | 2 +- .../themes/{common/atelier-common.ts => atelier/common.ts} | 2 +- styles/src/themes/ayu/ayu-dark.ts | 2 +- styles/src/themes/ayu/ayu-light.ts | 2 +- styles/src/themes/ayu/ayu-mirage.ts | 2 +- styles/src/themes/{common/ayu-common.ts => ayu/common.ts} | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename styles/src/themes/{common/atelier-common.ts => atelier/common.ts} (96%) rename styles/src/themes/{common/ayu-common.ts => ayu/common.ts} (95%) diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index e8d0687044..07eb26a591 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -4,7 +4,7 @@ import { ColorScheme, Meta } from "./themes/common/colorScheme" const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) -const IGNORE_ITEMS = ["staff", "common", "template.ts"] +const IGNORE_ITEMS = ["staff", "common", "common.ts"] const ACCEPT_EXTENSION = ".ts" function getAllTsFiles(directoryPath: string) { diff --git a/styles/src/themes/common/atelier-common.ts b/styles/src/themes/atelier/common.ts similarity index 96% rename from styles/src/themes/common/atelier-common.ts rename to styles/src/themes/atelier/common.ts index 08a915d019..7fb07f7a6e 100644 --- a/styles/src/themes/common/atelier-common.ts +++ b/styles/src/themes/atelier/common.ts @@ -1,4 +1,4 @@ -import { License, Meta, ThemeSyntax } from "./colorScheme" +import { License, Meta, ThemeSyntax } from "../common/colorScheme" export interface Variant { meta: Meta diff --git a/styles/src/themes/ayu/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts index eb837231fe..1dc663f161 100644 --- a/styles/src/themes/ayu/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts index 9c29449db5..2543521944 100644 --- a/styles/src/themes/ayu/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts index 52bf549392..2ada3678ee 100644 --- a/styles/src/themes/ayu/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/common/ayu-common.ts b/styles/src/themes/ayu/common.ts similarity index 95% rename from styles/src/themes/common/ayu-common.ts rename to styles/src/themes/ayu/common.ts index f08817ef49..2d3687801f 100644 --- a/styles/src/themes/common/ayu-common.ts +++ b/styles/src/themes/ayu/common.ts @@ -1,8 +1,8 @@ import { dark, light, mirage } from "ayu" -import { ThemeSyntax } from "./syntax" +import { ThemeSyntax } from "../common/syntax" import chroma from "chroma-js" -import { colorRamp } from "./ramps" -import { Meta } from "./colorScheme" +import { colorRamp } from "../common/ramps" +import { Meta } from "../common/colorScheme" export const ayu = { dark, From 14efc18ecac7a601ad8481cde656a5dc6123ef2b Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:11:54 +0200 Subject: [PATCH 016/131] refactor: add license file and check it exists --- styles/src/colorSchemes.ts | 33 ++++++++++++++++++------- styles/src/themes/common/colorScheme.ts | 5 ++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index 07eb26a591..74f1a2a03c 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -1,11 +1,12 @@ import fs from "fs" import path from "path" -import { ColorScheme, Meta } from "./themes/common/colorScheme" +import { ColorScheme, MetaAndLicense } from "./themes/common/colorScheme" const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) const IGNORE_ITEMS = ["staff", "common", "common.ts"] const ACCEPT_EXTENSION = ".ts" +const LICENSE_FILE_NAME = "LICENSE" function getAllTsFiles(directoryPath: string) { const files = fs.readdirSync(directoryPath) @@ -30,7 +31,9 @@ function getAllColorSchemes(directoryPath: string) { const files = getAllTsFiles(directoryPath) return files.map((filePath) => ({ colorScheme: require(filePath), - filePath: path.basename(filePath), + filePath, + fileName: path.basename(filePath), + licenseFile: `${path.dirname(filePath)}/${LICENSE_FILE_NAME}`, })) } @@ -45,15 +48,27 @@ function getColorSchemes(directoryPath: string) { return colorSchemes } -function getMeta(directoryPath: string) { - const meta: Meta[] = [] +function getMetaAndLicense(directoryPath: string) { + const meta: MetaAndLicense[] = [] - for (const { colorScheme, filePath } of getAllColorSchemes(directoryPath)) { - if (colorScheme.meta) { - meta.push(colorScheme.meta) - } else { + for (const { colorScheme, filePath, licenseFile } of getAllColorSchemes( + directoryPath + )) { + const licenseExists = fs.existsSync(licenseFile) + if (!licenseExists) { + throw Error( + `Public theme should have a LICENSE file ${licenseFile}` + ) + } + + if (!colorScheme.meta) { throw Error(`Public theme ${filePath} must have a meta field`) } + + meta.push({ + meta: colorScheme.meta, + licenseFile, + }) } return meta @@ -61,4 +76,4 @@ function getMeta(directoryPath: string) { export const colorSchemes = getColorSchemes(THEMES_DIRECTORY) export const staffColorSchemes = getColorSchemes(STAFF_DIRECTORY) -export const schemeMeta = getMeta(THEMES_DIRECTORY) +export const schemeMeta = getMetaAndLicense(THEMES_DIRECTORY) diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 3b5b8f6932..657250f5f2 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -19,6 +19,11 @@ export interface ColorScheme { syntax?: Partial } +export interface MetaAndLicense { + meta: Meta + licenseFile: string +} + export interface Meta { name: string author: string From 4568f80f2e7cbc7e822762bcf7d54e782998d02f Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:12:31 +0200 Subject: [PATCH 017/131] feat: read from LICENSE file --- styles/src/buildLicenses.ts | 78 ++++++++++--------------------------- 1 file changed, 20 insertions(+), 58 deletions(-) diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 3367e50ff0..9a053e9c0c 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -1,11 +1,9 @@ import * as fs from "fs" import toml from "toml" import { schemeMeta } from "./colorSchemes" -import { Meta, Verification } from "./themes/common/colorScheme" -import https from "https" -import crypto from "crypto" +import { MetaAndLicense } from "./themes/common/colorScheme" -const accepted_licenses_file = `${__dirname}/../../script/licenses/zed-licenses.toml` +const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml` // Use the cargo-about configuration file as the source of truth for supported licenses. function parseAcceptedToml(file: string): string[] { @@ -20,8 +18,11 @@ function parseAcceptedToml(file: string): string[] { return obj.accepted } -function checkLicenses(schemeMeta: Meta[], licenses: string[]) { - for (let meta of schemeMeta) { +function checkLicenses( + schemeMetaWithLicense: MetaAndLicense[], + licenses: string[] +) { + for (const { meta } of schemeMetaWithLicense) { // FIXME: Add support for conjuctions and conditions if (licenses.indexOf(meta.license.SPDX) < 0) { throw Error( @@ -31,62 +32,23 @@ function checkLicenses(schemeMeta: Meta[], licenses: string[]) { } } -function getLicenseText( - schemeMeta: Meta[], - callback: (meta: Meta, license_text: string) => void -) { - for (let meta of schemeMeta) { - if (typeof meta.license.license_text == "string") { - callback(meta, meta.license.license_text) - } else { - let license_text_obj: Verification = meta.license.license_text - // The following copied from the example code on nodejs.org: - // https://nodejs.org/api/http.html#httpgetoptions-callback - https - .get(license_text_obj.https_url, (res) => { - const { statusCode } = res - - if (statusCode < 200 || statusCode >= 300) { - throw new Error( - `Failed to fetch license for: ${meta.name}, Status Code: ${statusCode}` - ) - } - - res.setEncoding("utf8") - let rawData = "" - res.on("data", (chunk) => { - rawData += chunk - }) - res.on("end", () => { - const hash = crypto - .createHash("sha256") - .update(rawData) - .digest("hex") - if (license_text_obj.license_checksum == hash) { - callback(meta, rawData) - } else { - throw Error( - `Checksum for ${meta.name} did not match file downloaded from ${license_text_obj.https_url}` - ) - } - }) - }) - .on("error", (e) => { - throw e - }) - } +function generateLicenseFile(schemeMetaWithLicense: MetaAndLicense[]) { + for (const { meta, licenseFile } of schemeMetaWithLicense) { + const licenseText = fs.readFileSync(licenseFile).toString() + writeLicense(meta.name, meta.url, licenseText) } } -function writeLicense(schemeMeta: Meta, text: String) { +function writeLicense( + themeName: string, + themeUrl: string, + licenseText: String +) { process.stdout.write( - `## [${schemeMeta.name}](${schemeMeta.url})\n\n${text}\n********************************************************************************\n\n` + `## [${themeName}](${themeUrl})\n\n${licenseText}\n********************************************************************************\n\n` ) } -const accepted_licenses = parseAcceptedToml(accepted_licenses_file) -checkLicenses(schemeMeta, accepted_licenses) - -getLicenseText(schemeMeta, (meta, text) => { - writeLicense(meta, text) -}) +const acceptedLicenses = parseAcceptedToml(ACCEPTED_LICENSES_FILE) +checkLicenses(schemeMeta, acceptedLicenses) +generateLicenseFile(schemeMeta) From 51c7078f47827e3701c275e501bc2a040799d32b Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:16:50 +0200 Subject: [PATCH 018/131] chore: remove license_text --- styles/src/themes/andromeda/andromeda.ts | 6 ------ styles/src/themes/atelier/common.ts | 5 ----- styles/src/themes/ayu/common.ts | 6 ------ styles/src/themes/common/colorScheme.ts | 7 ------- styles/src/themes/gruvbox/gruvbox-common.ts | 2 -- styles/src/themes/one/one-dark.ts | 6 ------ styles/src/themes/one/one-light.ts | 6 ------ styles/src/themes/rose-pine/rose-pine-dawn.ts | 6 ------ styles/src/themes/rose-pine/rose-pine-moon.ts | 6 ------ styles/src/themes/rose-pine/rose-pine.ts | 6 ------ styles/src/themes/sandcastle/sandcastle.ts | 6 ------ styles/src/themes/solarized/solarized.ts | 6 ------ styles/src/themes/summercamp/summercamp.ts | 6 ------ 13 files changed, 74 deletions(-) diff --git a/styles/src/themes/andromeda/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts index adfea37d06..d7f7f53b90 100644 --- a/styles/src/themes/andromeda/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "EliverLara", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/EliverLara/Andromeda/master/LICENSE.md", - license_checksum: - "2f7886f1a05cefc2c26f5e49de1a39fa4466413c1ccb06fc80960e73f5ed4b89", - }, }, url: "https://github.com/EliverLara/Andromeda", } diff --git a/styles/src/themes/atelier/common.ts b/styles/src/themes/atelier/common.ts index 7fb07f7a6e..746834c88b 100644 --- a/styles/src/themes/atelier/common.ts +++ b/styles/src/themes/atelier/common.ts @@ -29,11 +29,6 @@ export const metaCommon: { author: "Bram de Haan (http://atelierbramdehaan.nl)", license: { SPDX: "MIT", - license_text: { - https_url: "https://atelierbram.mit-license.org/license.txt", - license_checksum: - "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", - }, }, } diff --git a/styles/src/themes/ayu/common.ts b/styles/src/themes/ayu/common.ts index 2d3687801f..e586e2d22d 100644 --- a/styles/src/themes/ayu/common.ts +++ b/styles/src/themes/ayu/common.ts @@ -79,12 +79,6 @@ export const meta: Meta = { author: "dempfi", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/dempfi/ayu/master/LICENSE", - license_checksum: - "e0af0e0d1754c18ca075649d42f5c6d9a60f8bdc03c20dfd97105f2253a94173", - }, }, url: "https://github.com/dempfi/ayu", } diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 657250f5f2..5cf125ae8f 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -33,13 +33,6 @@ export interface Meta { export interface License { SPDX: SPDXExpression - /// A url where we can download the license's text - license_text: Verification | string -} - -export interface Verification { - https_url: string - license_checksum: string } // License name -> License text diff --git a/styles/src/themes/gruvbox/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts index 5958fe62ac..b113ce68c6 100644 --- a/styles/src/themes/gruvbox/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -248,8 +248,6 @@ export const meta: Meta = { name, license: { SPDX: "MIT", // "MIT/X11" - license_text: - "Copyright \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/ or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", }, author: "morhetz ", url: "https://github.com/morhetz/gruvbox", diff --git a/styles/src/themes/one/one-dark.ts b/styles/src/themes/one/one-dark.ts index 71062940a5..1a88ddf7dc 100644 --- a/styles/src/themes/one/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -74,12 +74,6 @@ export const meta: Meta = { author: "simurai", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", - license_checksum: - "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - }, }, url: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", } diff --git a/styles/src/themes/one/one-light.ts b/styles/src/themes/one/one-light.ts index 8ae42e8b1d..231ee3abb3 100644 --- a/styles/src/themes/one/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -73,12 +73,6 @@ export const meta: Meta = { author: "simurai", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", - license_checksum: - "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - }, }, url: "https://github.com/atom/atom/tree/master/packages/one-light-ui", } diff --git a/styles/src/themes/rose-pine/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts index 48da855304..cf953872e1 100644 --- a/styles/src/themes/rose-pine/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts index 2e13ca4b70..85ac5fd73e 100644 --- a/styles/src/themes/rose-pine/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts index 33d7d533e6..fe2817be13 100644 --- a/styles/src/themes/rose-pine/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -32,12 +32,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/sandcastle/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts index 0b26521256..2544b6399a 100644 --- a/styles/src/themes/sandcastle/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -32,12 +32,6 @@ export const meta: Meta = { author: "gessig", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/gessig/base16-sandcastle-scheme/master/LICENSE", - license_checksum: - "8399d44b4d935b60be9fee0a76d7cc9a817b4f3f11574c9d6d1e8fd57e72ffdc", - }, }, url: "https://github.com/gessig/base16-sandcastle-scheme", } diff --git a/styles/src/themes/solarized/solarized.ts b/styles/src/themes/solarized/solarized.ts index 13e3e34460..6c826fbee7 100644 --- a/styles/src/themes/solarized/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -35,12 +35,6 @@ export const meta: Metadata = { author: "Ethan Schoonover", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/altercation/solarized/master/LICENSE", - license_checksum: - "494aefdabf86acce06bd63001ad8aedad4ee38da23509d3f917d95aa3368b9a6", - }, }, url: "https://github.com/altercation/solarized", } diff --git a/styles/src/themes/summercamp/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts index 41e101af02..94c0072e4f 100644 --- a/styles/src/themes/summercamp/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -34,11 +34,5 @@ export const meta: Meta = { url: "https://github.com/zoefiri/base16-sc", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/zoefiri/base16-sc/master/LICENSE", - license_checksum: - "fadcc834b7eaf2943800956600e8aeea4b495ecf6490f4c4b6c91556a90accaf", - }, }, } From c9820fde61acd5673dda49c3e4144b8e6bfd1b3b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 17:48:41 -0700 Subject: [PATCH 019/131] WIP: Add toast when users attempt to use shift-escape for the first time --- crates/util/src/channel.rs | 2 + crates/workspace/src/notifications.rs | 166 ++++++++++++++------------ crates/workspace/src/pane.rs | 10 +- crates/workspace/src/workspace.rs | 60 +++++++++- crates/zed/src/main.rs | 1 + 5 files changed, 157 insertions(+), 82 deletions(-) diff --git a/crates/util/src/channel.rs b/crates/util/src/channel.rs index 274fd576a0..2b45cb6b66 100644 --- a/crates/util/src/channel.rs +++ b/crates/util/src/channel.rs @@ -2,6 +2,8 @@ use std::env; use lazy_static::lazy_static; +pub struct ZedVersion(pub &'static str); + lazy_static! { pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { env::var("ZED_RELEASE_CHANNEL") diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 21b3be09d0..4437a960ef 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -1,5 +1,5 @@ use crate::{Toast, Workspace}; -use collections::HashSet; +use collections::HashMap; use gpui::{AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle}; use std::{any::TypeId, ops::DerefMut}; @@ -34,11 +34,11 @@ impl From<&dyn NotificationHandle> for AnyViewHandle { } struct NotificationTracker { - notifications_sent: HashSet, + notifications_sent: HashMap>, } impl std::ops::Deref for NotificationTracker { - type Target = HashSet; + type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.notifications_sent @@ -54,24 +54,35 @@ impl DerefMut for NotificationTracker { impl NotificationTracker { fn new() -> Self { Self { - notifications_sent: HashSet::default(), + notifications_sent: Default::default(), } } } impl Workspace { + pub fn has_shown_notification_once( + &self, + id: usize, + cx: &ViewContext, + ) -> bool { + cx + .global::() + .get(&TypeId::of::()) + .map(|ids| ids.contains(&id)) + .unwrap_or(false) + } + pub fn show_notification_once( &mut self, id: usize, cx: &mut ViewContext, build_notification: impl FnOnce(&mut ViewContext) -> ViewHandle, ) { - if !cx - .global::() - .contains(&TypeId::of::()) + if !self.has_shown_notification_once::(id, cx) { cx.update_global::(|tracker, _| { - tracker.insert(TypeId::of::()) + let entry = tracker.entry(TypeId::of::()).or_default(); + entry.push(id); }); self.show_notification::(id, cx, build_notification) @@ -247,80 +258,81 @@ pub mod simple_message_notification { let on_click = self.on_click.clone(); let has_click_action = on_click.is_some(); - MouseEventHandler::::new(0, cx, |state, cx| { - Flex::column() - .with_child( - Flex::row() - .with_child( - Text::new(message, theme.message.text.clone()) - .contained() - .with_style(theme.message.container) - .aligned() - .top() - .left() - .flex(1., true), - ) - .with_child( - MouseEventHandler::::new(0, cx, |state, _| { - let style = theme.dismiss_button.style_for(state, false); - Svg::new("icons/x_mark_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - }) - .with_padding(Padding::uniform(5.)) - .on_click(MouseButton::Left, move |_, this, cx| { - this.dismiss(&Default::default(), cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .aligned() - .constrained() - .with_height( - cx.font_cache().line_height(theme.message.text.font_size), - ) + Flex::column() + .with_child( + Flex::row() + .with_child( + Text::new(message, theme.message.text.clone()) + .contained() + .with_style(theme.message.container) .aligned() .top() - .flex_float(), - ), - ) - .with_children({ - let style = theme.action_message.style_for(state, false); - if let Some(click_message) = click_message { - Some( - Flex::row().with_child( - Text::new(click_message, style.text.clone()) + .left() + .flex(1., true), + ) + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.dismiss_button.style_for(state, false); + Svg::new("icons/x_mark_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + }) + .with_padding(Padding::uniform(5.)) + .on_click(MouseButton::Left, move |_, this, cx| { + this.dismiss(&Default::default(), cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .aligned() + .constrained() + .with_height(cx.font_cache().line_height(theme.message.text.font_size)) + .aligned() + .top() + .flex_float(), + ), + ) + .with_children({ + click_message + .map(|click_message| { + MouseEventHandler::::new( + 0, + cx, + |state, _| { + let style = theme.action_message.style_for(state, false); + + Flex::row() + .with_child( + Text::new(click_message, style.text.clone()) + .contained() + .with_style(style.container), + ) .contained() - .with_style(style.container), - ), + }, ) - } else { - None - } + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(on_click) = on_click.as_ref() { + on_click(cx); + this.dismiss(&Default::default(), cx); + } + }) + // Since we're not using a proper overlay, we have to capture these extra events + .on_down(MouseButton::Left, |_, _, _| {}) + .on_up(MouseButton::Left, |_, _, _| {}) + .with_cursor_style(if has_click_action { + CursorStyle::PointingHand + } else { + CursorStyle::Arrow + }) + }) .into_iter() - }) - .contained() - }) - // Since we're not using a proper overlay, we have to capture these extra events - .on_down(MouseButton::Left, |_, _, _| {}) - .on_up(MouseButton::Left, |_, _, _| {}) - .on_click(MouseButton::Left, move |_, this, cx| { - if let Some(on_click) = on_click.as_ref() { - on_click(cx); - this.dismiss(&Default::default(), cx); - } - }) - .with_cursor_style(if has_click_action { - CursorStyle::PointingHand - } else { - CursorStyle::Arrow - }) - .into_any() + }) + .into_any() } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 921ae5e010..fad8cd8864 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,8 +2,8 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewCenterTerminal, NewFile, - NewSearch, ToggleZoom, Workspace, WorkspaceSettings, + item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item, + NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; @@ -536,6 +536,12 @@ impl Pane { } pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + // Potentially warn the user of the new keybinding + let workspace_handle = self.workspace().clone(); + cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) + .detach(); + + if self.zoomed { cx.emit(Event::ZoomOut); } else if !self.items.is_empty() { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d051f6d80a..292ec28abc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, + Client, TypedEnvelope, UserStore, ZED_APP_VERSION, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -83,7 +83,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::Theme; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{async_iife, paths, ResultExt}; +use util::{async_iife, channel::ZedVersion, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; lazy_static! { @@ -3190,6 +3190,60 @@ async fn open_items( opened_items } +fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { + const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; + const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; + + if workspace + .read_with(cx, |workspace, cx| { + let version = cx.global::().0; + if !version.contains("0.88") + && !version.contains("0.89") + && !version.contains("0.90") + && !version.contains("0.91") + && !version.contains("0.92") + { + return true; + } + workspace.has_shown_notification_once::(2, cx) + }) + .unwrap_or(false) + { + return; + } + + if db::kvp::KEY_VALUE_STORE + .read_kvp(NEW_DOCK_HINT_KEY) + .ok() + .flatten() + .is_some() + { + return; + } + + cx.spawn(|_| async move { + db::kvp::KEY_VALUE_STORE + .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string()) + .await + .ok(); + }) + .detach(); + + workspace + .update(cx, |workspace, cx| { + workspace.show_notification_once(2, cx, |cx| { + cx.add_view(|_| { + MessageNotification::new( + "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", + ) + .with_click_message("Click to read more about the new panel system") + .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) + }) + }) + }) + .ok(); +} + fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; @@ -3206,7 +3260,7 @@ fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut Asy } else { let backup_path = (*db::BACKUP_DB_PATH).read(); if let Some(backup_path) = backup_path.clone() { - workspace.show_notification_once(0, cx, move |cx| { + workspace.show_notification_once(1, cx, move |cx| { cx.add_view(move |_| { MessageNotification::new(format!( "Database file was corrupted. Old database backed up to {}", diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 31f331ef93..558796807b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -119,6 +119,7 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); + cx.set_global(util::channel::ZedVersion(env!("CARGO_PKG_VERSION"))); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); From 8f95435548ee3ef34085992588e4f4107bf1b346 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 30 May 2023 11:05:08 -0700 Subject: [PATCH 020/131] Replicate project-specific settings when collaborating --- .../20221109000000_test_schema.sql | 10 ++ ...0529164700_add_worktree_settings_files.sql | 10 ++ crates/collab/src/db.rs | 101 ++++++++++++++ .../collab/src/db/worktree_settings_file.rs | 19 +++ crates/collab/src/rpc.rs | 50 +++++++ crates/collab/src/tests/integration_tests.rs | 129 ++++++++++++++++++ crates/project/src/project.rs | 105 +++++++++++--- crates/rpc/proto/zed.proto | 9 ++ crates/rpc/src/proto.rs | 2 + crates/settings/src/settings_store.rs | 15 ++ 10 files changed, 432 insertions(+), 18 deletions(-) create mode 100644 crates/collab/migrations/20230529164700_add_worktree_settings_files.sql create mode 100644 crates/collab/src/db/worktree_settings_file.rs diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index 7c6a49f179..b0b2a684d9 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -112,6 +112,16 @@ CREATE INDEX "index_worktree_repository_statuses_on_project_id" ON "worktree_rep CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id" ON "worktree_repository_statuses" ("project_id", "worktree_id"); CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id_and_work_directory_id" ON "worktree_repository_statuses" ("project_id", "worktree_id", "work_directory_id"); +CREATE TABLE "worktree_settings_files" ( + "project_id" INTEGER NOT NULL, + "worktree_id" INTEGER NOT NULL, + "path" VARCHAR NOT NULL, + "content" TEXT, + PRIMARY KEY(project_id, worktree_id, path), + FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE +); +CREATE INDEX "index_worktree_settings_files_on_project_id" ON "worktree_settings_files" ("project_id"); +CREATE INDEX "index_worktree_settings_files_on_project_id_and_worktree_id" ON "worktree_settings_files" ("project_id", "worktree_id"); CREATE TABLE "worktree_diagnostic_summaries" ( "project_id" INTEGER NOT NULL, diff --git a/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql b/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql new file mode 100644 index 0000000000..973a40af0f --- /dev/null +++ b/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql @@ -0,0 +1,10 @@ +CREATE TABLE "worktree_settings_files" ( + "project_id" INTEGER NOT NULL, + "worktree_id" INT8 NOT NULL, + "path" VARCHAR NOT NULL, + "content" TEXT NOT NULL, + PRIMARY KEY(project_id, worktree_id, path), + FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE +); +CREATE INDEX "index_settings_files_on_project_id" ON "worktree_settings_files" ("project_id"); +CREATE INDEX "index_settings_files_on_project_id_and_wt_id" ON "worktree_settings_files" ("project_id", "worktree_id"); diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index fd28fb9101..1deca1baa8 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -16,6 +16,7 @@ mod worktree_diagnostic_summary; mod worktree_entry; mod worktree_repository; mod worktree_repository_statuses; +mod worktree_settings_file; use crate::executor::Executor; use crate::{Error, Result}; @@ -1494,6 +1495,7 @@ impl Database { updated_repositories: Default::default(), removed_repositories: Default::default(), diagnostic_summaries: Default::default(), + settings_files: Default::default(), scan_id: db_worktree.scan_id as u64, completed_scan_id: db_worktree.completed_scan_id as u64, }; @@ -1638,6 +1640,25 @@ impl Database { }) .collect::>(); + { + let mut db_settings_files = worktree_settings_file::Entity::find() + .filter(worktree_settings_file::Column::ProjectId.eq(project_id)) + .stream(&*tx) + .await?; + while let Some(db_settings_file) = db_settings_files.next().await { + let db_settings_file = db_settings_file?; + if let Some(worktree) = worktrees + .iter_mut() + .find(|w| w.id == db_settings_file.worktree_id as u64) + { + worktree.settings_files.push(WorktreeSettingsFile { + path: db_settings_file.path, + content: db_settings_file.content, + }); + } + } + } + let mut collaborators = project .find_related(project_collaborator::Entity) .all(&*tx) @@ -2637,6 +2658,58 @@ impl Database { .await } + pub async fn update_worktree_settings( + &self, + update: &proto::UpdateWorktreeSettings, + connection: ConnectionId, + ) -> Result>> { + let project_id = ProjectId::from_proto(update.project_id); + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + // Ensure the update comes from the host. + let project = project::Entity::find_by_id(project_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + if project.host_connection()? != connection { + return Err(anyhow!("can't update a project hosted by someone else"))?; + } + + if let Some(content) = &update.content { + worktree_settings_file::Entity::insert(worktree_settings_file::ActiveModel { + project_id: ActiveValue::Set(project_id), + worktree_id: ActiveValue::Set(update.worktree_id as i64), + path: ActiveValue::Set(update.path.clone()), + content: ActiveValue::Set(content.clone()), + }) + .on_conflict( + OnConflict::columns([ + worktree_settings_file::Column::ProjectId, + worktree_settings_file::Column::WorktreeId, + worktree_settings_file::Column::Path, + ]) + .update_column(worktree_settings_file::Column::Content) + .to_owned(), + ) + .exec(&*tx) + .await?; + } else { + worktree_settings_file::Entity::delete(worktree_settings_file::ActiveModel { + project_id: ActiveValue::Set(project_id), + worktree_id: ActiveValue::Set(update.worktree_id as i64), + path: ActiveValue::Set(update.path.clone()), + ..Default::default() + }) + .exec(&*tx) + .await?; + } + + let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; + Ok(connection_ids) + }) + .await + } + pub async fn join_project( &self, project_id: ProjectId, @@ -2707,6 +2780,7 @@ impl Database { entries: Default::default(), repository_entries: Default::default(), diagnostic_summaries: Default::default(), + settings_files: Default::default(), scan_id: db_worktree.scan_id as u64, completed_scan_id: db_worktree.completed_scan_id as u64, }, @@ -2819,6 +2893,25 @@ impl Database { } } + // Populate worktree settings files + { + let mut db_settings_files = worktree_settings_file::Entity::find() + .filter(worktree_settings_file::Column::ProjectId.eq(project_id)) + .stream(&*tx) + .await?; + while let Some(db_settings_file) = db_settings_files.next().await { + let db_settings_file = db_settings_file?; + if let Some(worktree) = + worktrees.get_mut(&(db_settings_file.worktree_id as u64)) + { + worktree.settings_files.push(WorktreeSettingsFile { + path: db_settings_file.path, + content: db_settings_file.content, + }); + } + } + } + // Populate language servers. let language_servers = project .find_related(language_server::Entity) @@ -3482,6 +3575,7 @@ pub struct RejoinedWorktree { pub updated_repositories: Vec, pub removed_repositories: Vec, pub diagnostic_summaries: Vec, + pub settings_files: Vec, pub scan_id: u64, pub completed_scan_id: u64, } @@ -3537,10 +3631,17 @@ pub struct Worktree { pub entries: Vec, pub repository_entries: BTreeMap, pub diagnostic_summaries: Vec, + pub settings_files: Vec, pub scan_id: u64, pub completed_scan_id: u64, } +#[derive(Debug)] +pub struct WorktreeSettingsFile { + pub path: String, + pub content: String, +} + #[cfg(test)] pub use test::*; diff --git a/crates/collab/src/db/worktree_settings_file.rs b/crates/collab/src/db/worktree_settings_file.rs new file mode 100644 index 0000000000..f8e87f6e59 --- /dev/null +++ b/crates/collab/src/db/worktree_settings_file.rs @@ -0,0 +1,19 @@ +use super::ProjectId; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "worktree_settings_files")] +pub struct Model { + #[sea_orm(primary_key)] + pub project_id: ProjectId, + #[sea_orm(primary_key)] + pub worktree_id: i64, + #[sea_orm(primary_key)] + pub path: String, + pub content: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4c117b613d..61c6123a82 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -200,6 +200,7 @@ impl Server { .add_message_handler(start_language_server) .add_message_handler(update_language_server) .add_message_handler(update_diagnostic_summary) + .add_message_handler(update_worktree_settings) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) @@ -1088,6 +1089,18 @@ async fn rejoin_room( }, )?; } + + for settings_file in worktree.settings_files { + session.peer.send( + session.connection_id, + proto::UpdateWorktreeSettings { + project_id: project.id.to_proto(), + worktree_id: worktree.id, + path: settings_file.path, + content: Some(settings_file.content), + }, + )?; + } } for language_server in &project.language_servers { @@ -1410,6 +1423,18 @@ async fn join_project( }, )?; } + + for settings_file in dbg!(worktree.settings_files) { + session.peer.send( + session.connection_id, + proto::UpdateWorktreeSettings { + project_id: project_id.to_proto(), + worktree_id: worktree.id, + path: settings_file.path, + content: Some(settings_file.content), + }, + )?; + } } for language_server in &project.language_servers { @@ -1525,6 +1550,31 @@ async fn update_diagnostic_summary( Ok(()) } +async fn update_worktree_settings( + message: proto::UpdateWorktreeSettings, + session: Session, +) -> Result<()> { + dbg!(&message); + + let guest_connection_ids = session + .db() + .await + .update_worktree_settings(&message, session.connection_id) + .await?; + + broadcast( + Some(session.connection_id), + guest_connection_ids.iter().copied(), + |connection_id| { + session + .peer + .forward_send(session.connection_id, connection_id, message.clone()) + }, + ); + + Ok(()) +} + async fn start_language_server( request: proto::StartLanguageServer, session: Session, diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index d771f969d8..fb0f63b243 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3114,6 +3114,135 @@ async fn test_fs_operations( }); } +#[gpui::test(iterations = 10)] +async fn test_local_settings( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(&deterministic).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + let active_call_a = cx_a.read(ActiveCall::global); + + // As client A, open a project that contains some local settings files + client_a + .fs + .insert_tree( + "/dir", + json!({ + ".zed": { + "settings.json": r#"{ "tab_size": 2 }"# + }, + "a": { + ".zed": { + "settings.json": r#"{ "tab_size": 8 }"# + }, + "a.txt": "a-contents", + }, + "b": { + "b.txt": "b-contents", + } + }), + ) + .await; + let (project_a, _) = client_a.build_local_project("/dir", cx_a).await; + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); + + // As client B, join that project and observe the local settings. + let project_b = client_b.build_remote_project(project_id, cx_b).await; + let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap()); + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("").into(), r#"{"tab_size":2}"#.to_string()), + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + ] + ) + }); + + // As client A, update a settings file. As Client B, see the changed settings. + client_a + .fs + .insert_file("/dir/.zed/settings.json", r#"{}"#.into()) + .await; + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("").into(), r#"{}"#.to_string()), + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + ] + ) + }); + + // As client A, create and remove some settings files. As client B, see the changed settings. + client_a + .fs + .remove_file("/dir/.zed/settings.json".as_ref(), Default::default()) + .await + .unwrap(); + client_a + .fs + .create_dir("/dir/b/.zed".as_ref()) + .await + .unwrap(); + client_a + .fs + .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into()) + .await; + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()), + ] + ) + }); + + // As client B, disconnect. + server.forbid_connections(); + server.disconnect_client(client_b.peer_id().unwrap()); + + // As client A, change and remove settings files while client B is disconnected. + client_a + .fs + .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into()) + .await; + client_a + .fs + .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default()) + .await + .unwrap(); + deterministic.run_until_parked(); + + // As client B, reconnect and see the changed settings. + server.allow_connections(); + deterministic.advance_clock(RECEIVE_TIMEOUT); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),] + ) + }); +} + #[gpui::test(iterations = 10)] async fn test_buffer_conflict_after_save( deterministic: Arc, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c76587ff56..3cc4a181c5 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -462,6 +462,7 @@ impl Project { client.add_model_request_handler(Self::handle_update_buffer); client.add_model_message_handler(Self::handle_update_diagnostic_summary); client.add_model_message_handler(Self::handle_update_worktree); + client.add_model_message_handler(Self::handle_update_worktree_settings); client.add_model_request_handler(Self::handle_create_project_entry); client.add_model_request_handler(Self::handle_rename_project_entry); client.add_model_request_handler(Self::handle_copy_project_entry); @@ -1105,6 +1106,21 @@ impl Project { .log_err(); } + let store = cx.global::(); + for worktree in self.worktrees(cx) { + let worktree_id = worktree.read(cx).id().to_proto(); + for (path, content) in store.local_settings(worktree.id()) { + self.client + .send(proto::UpdateWorktreeSettings { + project_id, + worktree_id, + path: path.to_string_lossy().into(), + content: Some(content), + }) + .log_err(); + } + } + let (updates_tx, mut updates_rx) = mpsc::unbounded(); let client = self.client.clone(); self.client_state = Some(ProjectClientState::Local { @@ -1217,6 +1233,14 @@ impl Project { message_id: u32, cx: &mut ModelContext, ) -> Result<()> { + cx.update_global::(|store, cx| { + for worktree in &self.worktrees { + store + .clear_local_settings(worktree.handle_id(), cx) + .log_err(); + } + }); + self.join_project_response_message_id = message_id; self.set_worktrees_from_proto(message.worktrees, cx)?; self.set_collaborators_from_proto(message.collaborators, cx)?; @@ -4888,8 +4912,12 @@ impl Project { .push(WorktreeHandle::Weak(worktree.downgrade())); } - cx.observe_release(worktree, |this, worktree, cx| { + let handle_id = worktree.id(); + cx.observe_release(worktree, move |this, worktree, cx| { let _ = this.remove_worktree(worktree.id(), cx); + cx.update_global::(|store, cx| { + store.clear_local_settings(handle_id, cx).log_err() + }); }) .detach(); @@ -5174,14 +5202,16 @@ impl Project { .detach(); } - pub fn update_local_worktree_settings( + fn update_local_worktree_settings( &mut self, worktree: &ModelHandle, changes: &UpdatedEntriesSet, cx: &mut ModelContext, ) { + let project_id = self.remote_id(); let worktree_id = worktree.id(); let worktree = worktree.read(cx).as_local().unwrap(); + let remote_worktree_id = worktree.id(); let mut settings_contents = Vec::new(); for (path, _, change) in changes.iter() { @@ -5195,10 +5225,7 @@ impl Project { let removed = *change == PathChange::Removed; let abs_path = worktree.absolutize(path); settings_contents.push(async move { - anyhow::Ok(( - settings_dir, - (!removed).then_some(fs.load(&abs_path).await?), - )) + (settings_dir, (!removed).then_some(fs.load(&abs_path).await)) }); } } @@ -5207,19 +5234,30 @@ impl Project { return; } + let client = self.client.clone(); cx.spawn_weak(move |_, mut cx| async move { - let settings_contents = futures::future::join_all(settings_contents).await; + let settings_contents: Vec<(Arc, _)> = + futures::future::join_all(settings_contents).await; cx.update(|cx| { cx.update_global::(|store, cx| { - for entry in settings_contents { - if let Some((directory, file_content)) = entry.log_err() { - store - .set_local_settings( - worktree_id, - directory, - file_content.as_ref().map(String::as_str), - cx, - ) + for (directory, file_content) in settings_contents { + let file_content = file_content.and_then(|content| content.log_err()); + store + .set_local_settings( + worktree_id, + directory.clone(), + file_content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + if let Some(remote_id) = project_id { + client + .send(proto::UpdateWorktreeSettings { + project_id: remote_id, + worktree_id: remote_worktree_id.to_proto(), + path: directory.to_string_lossy().into_owned(), + content: file_content, + }) .log_err(); } } @@ -5467,6 +5505,30 @@ impl Project { }) } + async fn handle_update_worktree_settings( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |this, cx| { + let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); + if let Some(worktree) = this.worktree_for_id(worktree_id, cx) { + cx.update_global::(|store, cx| { + store + .set_local_settings( + worktree.id(), + PathBuf::from(&envelope.payload.path).into(), + envelope.payload.content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + }); + } + Ok(()) + }) + } + async fn handle_create_project_entry( this: ModelHandle, envelope: TypedEnvelope, @@ -6557,8 +6619,8 @@ impl Project { } self.metadata_changed(cx); - for (id, _) in old_worktrees_by_id { - cx.emit(Event::WorktreeRemoved(id)); + for id in old_worktrees_by_id.keys() { + cx.emit(Event::WorktreeRemoved(*id)); } Ok(()) @@ -6928,6 +6990,13 @@ impl WorktreeHandle { WorktreeHandle::Weak(handle) => handle.upgrade(cx), } } + + pub fn handle_id(&self) -> usize { + match self { + WorktreeHandle::Strong(handle) => handle.id(), + WorktreeHandle::Weak(handle) => handle.id(), + } + } } impl OpenBuffer { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 848cc1c2fa..8351cdfd2d 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -132,6 +132,8 @@ message Envelope { OnTypeFormatting on_type_formatting = 111; OnTypeFormattingResponse on_type_formatting_response = 112; + + UpdateWorktreeSettings update_worktree_settings = 113; } } @@ -339,6 +341,13 @@ message UpdateWorktree { string abs_path = 10; } +message UpdateWorktreeSettings { + uint64 project_id = 1; + uint64 worktree_id = 2; + string path = 3; + optional string content = 4; +} + message CreateProjectEntry { uint64 project_id = 1; uint64 worktree_id = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 07925a0486..13794ea64d 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -236,6 +236,7 @@ messages!( (UpdateProject, Foreground), (UpdateProjectCollaborator, Foreground), (UpdateWorktree, Foreground), + (UpdateWorktreeSettings, Foreground), (UpdateDiffBase, Foreground), (GetPrivateUserInfo, Foreground), (GetPrivateUserInfoResponse, Foreground), @@ -345,6 +346,7 @@ entity_messages!( UpdateProject, UpdateProjectCollaborator, UpdateWorktree, + UpdateWorktreeSettings, UpdateDiffBase ); diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 2560d0b752..bb51fd1ed0 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -359,6 +359,21 @@ impl SettingsStore { Ok(()) } + /// Add or remove a set of local settings via a JSON string. + pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> { + eprintln!("clearing local settings {root_id}"); + self.local_deserialized_settings + .retain(|k, _| k.0 != root_id); + self.recompute_values(Some((root_id, "".as_ref())), cx)?; + Ok(()) + } + + pub fn local_settings(&self, root_id: usize) -> impl '_ + Iterator, String)> { + self.local_deserialized_settings + .range((root_id, Path::new("").into())..(root_id + 1, Path::new("").into())) + .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap())) + } + pub fn json_schema( &self, schema_params: &SettingsJsonSchemaParams, From eeba72d7755c6f1f2b4b1c8142b26212583deaec Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 09:03:45 -0700 Subject: [PATCH 021/131] Bump protocol version --- crates/rpc/src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index b929de9596..bef6efa529 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 56; +pub const PROTOCOL_VERSION: u32 = 57; From 76927b6d95bb8d7baeb38cdb39b59d365776e7c3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 10:29:59 -0700 Subject: [PATCH 022/131] Make active panel's button close its dock --- crates/workspace/src/dock.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 886afe943d..ac52c8f179 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -472,11 +472,22 @@ impl View for PanelButtons { Flex::row() .with_children(panels.into_iter().enumerate().map( |(panel_ix, (view, context_menu))| { - let (tooltip, tooltip_action) = view.icon_tooltip(cx); + let is_active = is_open && panel_ix == active_ix; + let (tooltip, tooltip_action) = if is_active { + ( + format!("Close {} dock", dock_position.to_label()), + Some(match dock_position { + DockPosition::Left => crate::ToggleLeftDock.boxed_clone(), + DockPosition::Bottom => crate::ToggleBottomDock.boxed_clone(), + DockPosition::Right => crate::ToggleRightDock.boxed_clone(), + }), + ) + } else { + view.icon_tooltip(cx) + }; Stack::new() .with_child( MouseEventHandler::::new(panel_ix, cx, |state, cx| { - let is_active = is_open && panel_ix == active_ix; let style = button_style.style_for(state, is_active); Flex::row() .with_child( From ac41564a8f3ac0c575bc29acaf57ede2a7e6753d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:34:06 -0700 Subject: [PATCH 023/131] Dismiss zoomed panels by closing their dock, not zooming them out --- crates/workspace/src/dock.rs | 4 ++ crates/workspace/src/workspace.rs | 110 ++++++++++++++++-------------- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index ac52c8f179..a4b71fa5a7 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -175,6 +175,10 @@ impl Dock { } } + pub fn position(&self) -> DockPosition { + self.position + } + pub fn is_open(&self) -> bool { self.is_open } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d051f6d80a..d721997053 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -908,18 +908,24 @@ impl Workspace { } }); } else if T::should_zoom_in_on_event(event) { - this.zoom_out(cx); 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)); } } else if T::should_zoom_out_on_event(event) { - this.zoom_out(cx); + dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx)); + if this.zoomed_position == Some(prev_position) { + this.zoomed = None; + this.zoomed_position = None; + } + cx.notify(); } else if T::is_focus_event(event) { + let position = panel.read(cx).position(cx); + this.dismiss_zoomed_items_to_reveal(Some(position), cx); if panel.is_zoomed(cx) { this.zoomed = Some(panel.downgrade().into_any()); - this.zoomed_position = Some(panel.read(cx).position(cx)); + this.zoomed_position = Some(position); } else { this.zoomed = None; this.zoomed_position = None; @@ -1592,7 +1598,7 @@ impl Workspace { DockPosition::Right => &self.right_dock, }; let mut focus_center = false; - let mut zoom_out = false; + let mut reveal_dock = false; dock.update(cx, |dock, cx| { let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); let was_visible = dock.is_open() && !other_is_zoomed; @@ -1607,14 +1613,15 @@ impl Workspace { if active_panel.is_zoomed(cx) { cx.focus(active_panel.as_any()); } - zoom_out = true; + reveal_dock = true; } } }); - if zoom_out { - self.zoom_out_everything_except(dock_side, cx); + if reveal_dock { + self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx); } + if focus_center { cx.focus_self(); } @@ -1623,62 +1630,49 @@ impl Workspace { self.serialize_workspace(cx); } + /// Transfer focus to the panel of the given type. pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { - self.show_or_hide_panel::(cx, |_, _| true)? + self.focus_or_unfocus_panel::(cx, |_, _| true)? .as_any() .clone() .downcast() } + /// Focus the panel of the given type if it isn't already focused. If it is + /// already focused, then transfer focus back to the workspace center. pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { - self.show_or_hide_panel::(cx, |panel, cx| !panel.has_focus(cx)); + self.focus_or_unfocus_panel::(cx, |panel, cx| !panel.has_focus(cx)); } - fn show_or_hide_panel( + /// Focus or unfocus the given panel type, depending on the given callback. + fn focus_or_unfocus_panel( &mut self, cx: &mut ViewContext, - show: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, + should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, ) -> Option> { - for (dock, position) in [ - self.left_dock.clone(), - self.bottom_dock.clone(), - self.right_dock.clone(), - ] - .into_iter() - .zip( - [ - DockPosition::Left, - DockPosition::Bottom, - DockPosition::Right, - ] - .into_iter(), - ) { + for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { let mut focus_center = false; - let mut zoom_out = false; + let mut reveal_dock = false; let panel = dock.update(cx, |dock, cx| { dock.activate_panel(panel_index, cx); let panel = dock.active_panel().cloned(); if let Some(panel) = panel.as_ref() { - let should_show = show(&**panel, cx); - if should_show { + if should_focus(&**panel, cx) { dock.set_open(true, cx); cx.focus(panel.as_any()); - zoom_out = true; + reveal_dock = true; } else { - if panel.is_zoomed(cx) { - dock.set_open(false, cx); - } + // if panel.is_zoomed(cx) { + // dock.set_open(false, cx); + // } focus_center = true; } } panel }); - if zoom_out { - self.zoom_out_everything_except(position, cx); - } if focus_center { cx.focus_self(); } @@ -1705,28 +1699,38 @@ impl Workspace { cx.notify(); } - fn zoom_out_everything_except( + fn dismiss_zoomed_items_to_reveal( &mut self, - except_position: DockPosition, + except_position: Option, cx: &mut ViewContext, ) { - for pane in &self.panes { - pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // If a center pane is zoomed, unzoom it. + if except_position.is_some() { + for pane in &self.panes { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + } } - if except_position != DockPosition::Left { - self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + // If another dock is zoomed, hide it. + let mut focus_center = false; + for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] { + dock.update(cx, |dock, cx| { + if Some(dock.position()) != except_position { + if let Some(panel) = dock.active_panel() { + if panel.is_zoomed(cx) { + focus_center |= panel.has_focus(cx); + dock.set_open(false, cx); + } + } + } + }); } - if except_position != DockPosition::Bottom { - self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + if focus_center { + cx.focus_self(); } - if except_position != DockPosition::Right { - self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); - } - - if self.zoomed_position != Some(except_position) { + if self.zoomed_position != except_position { self.zoomed = None; self.zoomed_position = None; } @@ -1937,6 +1941,7 @@ impl Workspace { self.last_active_center_pane = Some(pane.downgrade()); } + self.dismiss_zoomed_items_to_reveal(None, cx); if pane.read(cx).is_zoomed() { self.zoomed = Some(pane.downgrade().into_any()); } else { @@ -1998,7 +2003,6 @@ impl Workspace { } pane::Event::ZoomIn => { if pane == self.active_pane { - self.zoom_out(cx); pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); if pane.read(cx).has_focus() { self.zoomed = Some(pane.downgrade().into_any()); @@ -2007,7 +2011,13 @@ impl Workspace { cx.notify(); } } - pane::Event::ZoomOut => self.zoom_out(cx), + pane::Event::ZoomOut => { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + if self.zoomed_position.is_none() { + self.zoomed = None; + } + cx.notify(); + } } self.serialize_workspace(cx); From a10933c063307fb768bd122a86f1ff359c0e93fe Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 31 May 2023 12:59:04 -0400 Subject: [PATCH 024/131] Update pane/panel zoom styling --- styles/src/styleTree/workspace.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index cf5234aa00..ec992c9c0b 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -13,6 +13,7 @@ import tabBar from "./tabBar" export default function workspace(colorScheme: ColorScheme) { const layer = colorScheme.lowest + const isLight = colorScheme.isLight const itemSpacing = 8 const titlebarButton = { cornerRadius: 6, @@ -120,16 +121,18 @@ export default function workspace(colorScheme: ColorScheme) { }, zoomedBackground: { cursor: "Arrow", - background: withOpacity(background(colorScheme.lowest), 0.85) + background: isLight + ? withOpacity(background(colorScheme.lowest), 0.8) + : withOpacity(background(colorScheme.highest), 0.6) }, zoomedPaneForeground: { - margin: 10, + margin: 16, shadow: colorScheme.modalShadow, - border: border(colorScheme.highest, { overlay: true }), + border: border(colorScheme.lowest, { overlay: true }), }, zoomedPanelForeground: { - margin: 18, - border: border(colorScheme.highest, { overlay: true }), + margin: 16, + border: border(colorScheme.lowest, { overlay: true }), }, dock: { left: { From 1f72f9e18babcd8906d1d550ef4691831b3accaf Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 31 May 2023 12:59:28 -0400 Subject: [PATCH 025/131] Add un-hooked up zoomIcon styles --- styles/src/styleTree/tabBar.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index 61d30be7e0..ef1425cd15 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -31,6 +31,10 @@ export default function tabBar(colorScheme: ColorScheme) { iconClose: foreground(layer, "variant"), iconCloseActive: foreground(layer, "hovered"), + // Zoom Icons + iconZoom: foreground(layer, "variant"), + iconZoomActive: foreground(layer, "accent"), + // Indicators iconConflict: foreground(layer, "warning"), iconDirty: foreground(layer, "accent"), From 893615236dd721a235bf8afc3fcb446f3dcd5721 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:45:48 -0700 Subject: [PATCH 026/131] Only include one border edge for zoomed panels --- crates/workspace/src/workspace.rs | 56 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d721997053..1b2fdc80f4 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3288,32 +3288,36 @@ impl View for Workspace { enum ZoomBackground {} let zoomed = zoomed.upgrade(cx)?; - let mut foreground_style; - match self.zoomed_position { - Some(DockPosition::Left) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.left = 0.; - foreground_style.margin.top = 0.; - foreground_style.margin.bottom = 0.; - } - Some(DockPosition::Right) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.right = 0.; - foreground_style.margin.top = 0.; - foreground_style.margin.bottom = 0.; - } - Some(DockPosition::Bottom) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.left = 0.; - foreground_style.margin.right = 0.; - foreground_style.margin.bottom = 0.; - } - None => { - foreground_style = - theme.workspace.zoomed_pane_foreground; + let mut foreground_style = + theme.workspace.zoomed_pane_foreground; + if let Some(zoomed_dock_position) = self.zoomed_position { + foreground_style = + theme.workspace.zoomed_panel_foreground; + let margin = foreground_style.margin.top; + let border = foreground_style.border.top; + + // Only include a margin and border on the opposite side. + foreground_style.margin.top = 0.; + foreground_style.margin.left = 0.; + foreground_style.margin.bottom = 0.; + foreground_style.margin.right = 0.; + foreground_style.border.top = false; + foreground_style.border.left = false; + foreground_style.border.bottom = false; + foreground_style.border.right = false; + match zoomed_dock_position { + DockPosition::Left => { + foreground_style.margin.right = margin; + foreground_style.border.right = border; + } + DockPosition::Right => { + foreground_style.margin.left = margin; + foreground_style.border.left = border; + } + DockPosition::Bottom => { + foreground_style.margin.top = margin; + foreground_style.border.top = border; + } } } From 3f6aa94a5f4f3c49457f7c4fb96ac3a46d34f8e3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:51:57 -0700 Subject: [PATCH 027/131] Use active color for zoom button in a zoomed pane --- crates/terminal_view/src/terminal_panel.rs | 2 ++ crates/workspace/src/pane.rs | 6 +++++- styles/src/styleTree/tabBar.ts | 7 +++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index e45459e683..ac3875af9e 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -70,6 +70,7 @@ impl TerminalPanel { .with_child(Pane::render_tab_bar_button( 0, "icons/plus_12.svg", + false, Some(( "New Terminal".into(), Some(Box::new(workspace::NewTerminal)), @@ -94,6 +95,7 @@ impl TerminalPanel { } else { "icons/maximize_8.svg" }, + pane.is_zoomed(), Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), cx, move |pane, cx| pane.toggle_zoom(&Default::default(), cx), diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 921ae5e010..80a0256389 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -268,6 +268,7 @@ impl Pane { .with_child(Self::render_tab_bar_button( 0, "icons/plus_12.svg", + false, Some(("New...".into(), None)), cx, |pane, cx| pane.deploy_new_menu(cx), @@ -277,6 +278,7 @@ impl Pane { .with_child(Self::render_tab_bar_button( 1, "icons/split_12.svg", + false, Some(("Split Pane".into(), None)), cx, |pane, cx| pane.deploy_split_menu(cx), @@ -290,6 +292,7 @@ impl Pane { } else { "icons/maximize_8.svg" }, + pane.is_zoomed(), Some(("Toggle Zoom".into(), Some(Box::new(ToggleZoom)))), cx, move |pane, cx| pane.toggle_zoom(&Default::default(), cx), @@ -1401,6 +1404,7 @@ impl Pane { pub fn render_tab_bar_button)>( index: usize, icon: &'static str, + active: bool, tooltip: Option<(String, Option>)>, cx: &mut ViewContext, on_click: F, @@ -1410,7 +1414,7 @@ impl Pane { let mut button = MouseEventHandler::::new(index, cx, |mouse_state, cx| { let theme = &settings::get::(cx).theme.workspace.tab_bar; - let style = theme.pane_button.style_for(mouse_state, false); + let style = theme.pane_button.style_for(mouse_state, active); Svg::new(icon) .with_color(style.color) .constrained() diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index ef1425cd15..39a1ef0407 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -31,10 +31,6 @@ export default function tabBar(colorScheme: ColorScheme) { iconClose: foreground(layer, "variant"), iconCloseActive: foreground(layer, "hovered"), - // Zoom Icons - iconZoom: foreground(layer, "variant"), - iconZoomActive: foreground(layer, "accent"), - // Indicators iconConflict: foreground(layer, "warning"), iconDirty: foreground(layer, "accent"), @@ -98,6 +94,9 @@ export default function tabBar(colorScheme: ColorScheme) { hover: { color: foreground(layer, "hovered"), }, + active: { + color: foreground(layer, "accent"), + } }, paneButtonContainer: { background: tab.background, From c48fed26bdebdcb7573e7a06b1ac4fcd0068dc9e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 13:05:50 -0700 Subject: [PATCH 028/131] When revealing items, zoom-out any inactive panes --- crates/workspace/src/workspace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1b2fdc80f4..3d55a78e88 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1705,8 +1705,8 @@ impl Workspace { cx: &mut ViewContext, ) { // If a center pane is zoomed, unzoom it. - if except_position.is_some() { - for pane in &self.panes { + for pane in &self.panes { + if pane != &self.active_pane { pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); } } From 193474a346100b4496bc074b3a1efcf5c7cf611a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 13:07:20 -0700 Subject: [PATCH 029/131] :art: --- crates/workspace/src/workspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3d55a78e88..b00b8f1102 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1701,7 +1701,7 @@ impl Workspace { fn dismiss_zoomed_items_to_reveal( &mut self, - except_position: Option, + dock_to_reveal: Option, cx: &mut ViewContext, ) { // If a center pane is zoomed, unzoom it. @@ -1715,7 +1715,7 @@ impl Workspace { let mut focus_center = false; for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] { dock.update(cx, |dock, cx| { - if Some(dock.position()) != except_position { + if Some(dock.position()) != dock_to_reveal { if let Some(panel) = dock.active_panel() { if panel.is_zoomed(cx) { focus_center |= panel.has_focus(cx); @@ -1730,7 +1730,7 @@ impl Workspace { cx.focus_self(); } - if self.zoomed_position != except_position { + if self.zoomed_position != dock_to_reveal { self.zoomed = None; self.zoomed_position = None; } From ef80b539d150d67faa0dc821077364975eafea36 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 13:56:41 -0700 Subject: [PATCH 030/131] Fix notification styling, minimize database reads --- crates/workspace/src/notifications.rs | 38 ++++++++++++------ crates/workspace/src/workspace.rs | 57 +++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 4437a960ef..1060227d4a 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -33,7 +33,7 @@ impl From<&dyn NotificationHandle> for AnyViewHandle { } } -struct NotificationTracker { +pub(crate) struct NotificationTracker { notifications_sent: HashMap>, } @@ -65,8 +65,7 @@ impl Workspace { id: usize, cx: &ViewContext, ) -> bool { - cx - .global::() + cx.global::() .get(&TypeId::of::()) .map(|ids| ids.contains(&id)) .unwrap_or(false) @@ -78,8 +77,7 @@ impl Workspace { cx: &mut ViewContext, build_notification: impl FnOnce(&mut ViewContext) -> ViewHandle, ) { - if !self.has_shown_notification_once::(id, cx) - { + if !self.has_shown_notification_once::(id, cx) { cx.update_global::(|tracker, _| { let entry = tracker.entry(TypeId::of::()).or_default(); entry.push(id); @@ -167,7 +165,7 @@ pub mod simple_message_notification { elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, impl_actions, platform::{CursorStyle, MouseButton}, - AppContext, Element, Entity, View, ViewContext, + AnyElement, AppContext, Element, Entity, View, ViewContext, fonts::TextStyle, }; use menu::Cancel; use serde::Deserialize; @@ -195,8 +193,13 @@ pub mod simple_message_notification { ) } + enum NotificationMessage { + Text(Cow<'static, str>), + Element(fn(TextStyle, &AppContext) -> AnyElement), + } + pub struct MessageNotification { - message: Cow<'static, str>, + message: NotificationMessage, on_click: Option)>>, click_message: Option>, } @@ -215,7 +218,16 @@ pub mod simple_message_notification { S: Into>, { Self { - message: message.into(), + message: NotificationMessage::Text(message.into()), + on_click: None, + click_message: None, + } + } + + pub fn new_element(message: fn(TextStyle, &AppContext) -> AnyElement) -> MessageNotification + { + Self { + message: NotificationMessage::Element(message), on_click: None, click_message: None, } @@ -254,7 +266,12 @@ pub mod simple_message_notification { enum MessageNotificationTag {} let click_message = self.click_message.clone(); - let message = self.message.clone(); + let message = match &self.message { + NotificationMessage::Text(text) => { + Text::new(text.to_owned(), theme.message.text.clone()).into_any() + } + NotificationMessage::Element(e) => e(theme.message.text.clone(), cx), + }; let on_click = self.on_click.clone(); let has_click_action = on_click.is_some(); @@ -262,8 +279,7 @@ pub mod simple_message_notification { .with_child( Flex::row() .with_child( - Text::new(message, theme.message.text.clone()) - .contained() + message.contained() .with_style(theme.message.container) .aligned() .top() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 292ec28abc..823c9c9294 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, ZED_APP_VERSION, + Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -60,7 +60,7 @@ use std::{ }; use crate::{ - notifications::simple_message_notification::MessageNotification, + notifications::{simple_message_notification::MessageNotification, NotificationTracker}, persistence::model::{ DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace, }, @@ -81,7 +81,7 @@ use serde::Deserialize; use shared_screen::SharedScreen; use status_bar::StatusBar; pub use status_bar::StatusItemView; -use theme::Theme; +use theme::{Theme, ThemeSettings}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::{async_iife, channel::ZedVersion, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; @@ -3193,6 +3193,7 @@ async fn open_items( fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; + const MESSAGE_ID: usize = 2; if workspace .read_with(cx, |workspace, cx| { @@ -3205,7 +3206,7 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo { return true; } - workspace.has_shown_notification_once::(2, cx) + workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) { @@ -3218,6 +3219,24 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .flatten() .is_some() { + if !workspace + .read_with(cx, |workspace, cx| { + workspace.has_shown_notification_once::(MESSAGE_ID, cx) + }) + .unwrap_or(false) + { + cx.update(|cx| { + cx.update_global::(|tracker, _| { + let entry = tracker + .entry(TypeId::of::()) + .or_default(); + if !entry.contains(&MESSAGE_ID) { + entry.push(MESSAGE_ID); + } + }); + }); + } + return; } @@ -3232,11 +3251,33 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo workspace .update(cx, |workspace, cx| { workspace.show_notification_once(2, cx, |cx| { + + cx.add_view(|_| { - MessageNotification::new( - "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", - ) - .with_click_message("Click to read more about the new panel system") + MessageNotification::new_element(|text, _| { + Text::new( + "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.", + text, + ) + .with_custom_runs(vec![26..32, 34..46], |_, bounds, scene, cx| { + let code_span_background_color = settings::get::(cx) + .theme + .editor + .document_highlight_read_background; + + scene.push_quad(gpui::Quad { + bounds, + background: Some(code_span_background_color), + border: Default::default(), + corner_radius: 2.0, + }) + }) + .into_any() + }) + // MessageNotification::new_( + // "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", + // ) + .with_click_message("Read more about the new panel system") .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) }) }) From ed0b9acb0a5d16b363b42efbf7f1e2b30a2b95e7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:06:36 -0700 Subject: [PATCH 031/131] Add panic if version is 0.91 --- crates/workspace/src/notifications.rs | 11 +++++++---- crates/workspace/src/pane.rs | 1 - crates/workspace/src/workspace.rs | 14 ++------------ 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 1060227d4a..1e3c6044a1 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -163,9 +163,10 @@ pub mod simple_message_notification { use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, + fonts::TextStyle, impl_actions, platform::{CursorStyle, MouseButton}, - AnyElement, AppContext, Element, Entity, View, ViewContext, fonts::TextStyle, + AnyElement, AppContext, Element, Entity, View, ViewContext, }; use menu::Cancel; use serde::Deserialize; @@ -224,8 +225,9 @@ pub mod simple_message_notification { } } - pub fn new_element(message: fn(TextStyle, &AppContext) -> AnyElement) -> MessageNotification - { + pub fn new_element( + message: fn(TextStyle, &AppContext) -> AnyElement, + ) -> MessageNotification { Self { message: NotificationMessage::Element(message), on_click: None, @@ -279,7 +281,8 @@ pub mod simple_message_notification { .with_child( Flex::row() .with_child( - message.contained() + message + .contained() .with_style(theme.message.container) .aligned() .top() diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fad8cd8864..63602ffead 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -541,7 +541,6 @@ impl Pane { cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) .detach(); - if self.zoomed { cx.emit(Event::ZoomOut); } else if !self.items.is_empty() { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 823c9c9294..3690db60d0 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3198,13 +3198,8 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { let version = cx.global::().0; - if !version.contains("0.88") - && !version.contains("0.89") - && !version.contains("0.90") - && !version.contains("0.91") - && !version.contains("0.92") - { - return true; + if version.contains("0.91") { + panic!("Please remove the dock key binding change notification"); } workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) @@ -3251,8 +3246,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo workspace .update(cx, |workspace, cx| { workspace.show_notification_once(2, cx, |cx| { - - cx.add_view(|_| { MessageNotification::new_element(|text, _| { Text::new( @@ -3274,9 +3267,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo }) .into_any() }) - // MessageNotification::new_( - // "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", - // ) .with_click_message("Read more about the new panel system") .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) }) From b875d4ed40eac05bf6007a6d9f08f64ca65c446f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:10:50 -0700 Subject: [PATCH 032/131] Remove silly panic --- crates/workspace/src/workspace.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3690db60d0..73cc811e76 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3197,10 +3197,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { - let version = cx.global::().0; - if version.contains("0.91") { - panic!("Please remove the dock key binding change notification"); - } workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) From 705e36827c8e48dd0b2ccfb969806e41148ec935 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:16:37 -0700 Subject: [PATCH 033/131] add version check --- crates/util/src/channel.rs | 2 -- crates/workspace/src/workspace.rs | 12 ++++++++++-- crates/zed/src/main.rs | 1 - 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/util/src/channel.rs b/crates/util/src/channel.rs index 2b45cb6b66..274fd576a0 100644 --- a/crates/util/src/channel.rs +++ b/crates/util/src/channel.rs @@ -2,8 +2,6 @@ use std::env; use lazy_static::lazy_static; -pub struct ZedVersion(pub &'static str); - lazy_static! { pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { env::var("ZED_RELEASE_CHANNEL") diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 73cc811e76..7033530f96 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, + Client, TypedEnvelope, UserStore, ZED_APP_VERSION, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -83,7 +83,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::{Theme, ThemeSettings}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{async_iife, channel::ZedVersion, paths, ResultExt}; +use util::{async_iife, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; lazy_static! { @@ -3197,6 +3197,14 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { + let version = ZED_APP_VERSION + .or_else(|| cx.platform().app_version().ok()) + .map(|v| v.to_string()).unwrap_or_default(); + + if !version.contains("0.88") || !version.contains("0.89") || !version.contains("0.90") || !version.contains("0.91") { + return true; + } + workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 558796807b..31f331ef93 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -119,7 +119,6 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); - cx.set_global(util::channel::ZedVersion(env!("CARGO_PKG_VERSION"))); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); From 45b42c512d50b96f49c9722793572a0b484b2305 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:17:27 -0700 Subject: [PATCH 034/131] fmt --- crates/workspace/src/workspace.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7033530f96..c45cdf0c29 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3199,9 +3199,14 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .read_with(cx, |workspace, cx| { let version = ZED_APP_VERSION .or_else(|| cx.platform().app_version().ok()) - .map(|v| v.to_string()).unwrap_or_default(); + .map(|v| v.to_string()) + .unwrap_or_default(); - if !version.contains("0.88") || !version.contains("0.89") || !version.contains("0.90") || !version.contains("0.91") { + if !version.contains("0.88") + || !version.contains("0.89") + || !version.contains("0.90") + || !version.contains("0.91") + { return true; } From 27ef0e2b524755515eab87d37898f6759f15e2d3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:21:38 -0700 Subject: [PATCH 035/131] De-morgans properly --- crates/workspace/src/workspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c45cdf0c29..5d1bc2993d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3203,9 +3203,9 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .unwrap_or_default(); if !version.contains("0.88") - || !version.contains("0.89") - || !version.contains("0.90") - || !version.contains("0.91") + && !version.contains("0.89") + && !version.contains("0.90") + && !version.contains("0.91") { return true; } From bf2016adf504637b168b3fca76d0e9fb955ea293 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:22:49 -0700 Subject: [PATCH 036/131] Remove version check --- crates/workspace/src/workspace.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5d1bc2993d..91824f5855 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, ZED_APP_VERSION, + Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -3197,19 +3197,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { - let version = ZED_APP_VERSION - .or_else(|| cx.platform().app_version().ok()) - .map(|v| v.to_string()) - .unwrap_or_default(); - - if !version.contains("0.88") - && !version.contains("0.89") - && !version.contains("0.90") - && !version.contains("0.91") - { - return true; - } - workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) From a2ab7c9eb95f4970b0c2222e4494c83e2cb2779b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:42:15 -0700 Subject: [PATCH 037/131] Respect project-specific settings for copilot --- crates/copilot/src/copilot.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 32 +++++++++++-------- crates/editor/src/editor.rs | 15 +++++---- crates/language/src/language_settings.rs | 10 ++++-- crates/project/src/project.rs | 35 +++++++++++---------- crates/project/src/worktree.rs | 2 +- 6 files changed, 56 insertions(+), 40 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 88b0aebd17..1084222648 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -318,7 +318,7 @@ impl Copilot { fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext) { let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); - if all_language_settings(cx).copilot_enabled(None, None) { + if all_language_settings(None, cx).copilot_enabled(None, None) { if matches!(self.server, CopilotServer::Disabled) { let start_task = cx .spawn({ diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 686b3d2dfb..4cb7c15bd9 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -9,7 +9,10 @@ use gpui::{ AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; -use language::language_settings::{self, all_language_settings, AllLanguageSettings}; +use language::{ + language_settings::{self, all_language_settings, AllLanguageSettings}, + File, +}; use settings::{update_settings_file, SettingsStore}; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; @@ -27,7 +30,7 @@ pub struct CopilotButton { editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, language: Option>, - path: Option>, + file: Option>, fs: Arc, } @@ -41,7 +44,7 @@ impl View for CopilotButton { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let all_language_settings = &all_language_settings(cx); + let all_language_settings = all_language_settings(None, cx); if !all_language_settings.copilot.feature_enabled { return Empty::new().into_any(); } @@ -165,7 +168,7 @@ impl CopilotButton { editor_subscription: None, editor_enabled: None, language: None, - path: None, + file: None, fs, } } @@ -212,9 +215,9 @@ impl CopilotButton { let settings = settings::get::(cx); - if let Some(path) = self.path.as_ref() { - let path_enabled = settings.copilot_enabled_for_path(path); - let path = path.clone(); + if let Some(file) = &self.file { + let path = file.path().clone(); + let path_enabled = settings.copilot_enabled_for_path(&path); menu_options.push(ContextMenuItem::handler( format!( "{} Suggestions for This Path", @@ -279,14 +282,16 @@ impl CopilotButton { let language_name = snapshot .language_at(suggestion_anchor) .map(|language| language.name()); - let path = snapshot.file_at(suggestion_anchor).map(|file| file.path()); + let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(cx) - .copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())), + all_language_settings(self.file.as_ref().map(|f| f.as_ref()), cx).copilot_enabled( + language_name.as_deref(), + file.as_ref().map(|file| file.path().as_ref()), + ), ); self.language = language_name; - self.path = path.cloned(); + self.file = file; cx.notify() } @@ -363,14 +368,15 @@ async fn configure_disabled_globs( } fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(None, None); + let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None); update_settings_file::(fs, cx, move |file| { file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(Some(&language), None); + let show_copilot_suggestions = + all_language_settings(None, cx).copilot_enabled(Some(&language), None); update_settings_file::(fs, cx, move |file| { file.languages .entry(language) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f5d109e15b..4a9237f563 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3207,12 +3207,12 @@ impl Editor { snapshot: &MultiBufferSnapshot, cx: &mut ViewContext, ) -> bool { - let path = snapshot.file_at(location).map(|file| file.path().as_ref()); + let file = snapshot.file_at(location); let language_name = snapshot .language_at(location) .map(|language| language.name()); - let settings = all_language_settings(cx); - settings.copilot_enabled(language_name.as_deref(), path) + let settings = all_language_settings(file.map(|f| f.as_ref() as _), cx); + settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) } fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { @@ -7076,11 +7076,13 @@ impl Editor { }; // If None, we are in a file without an extension - let file_extension = file_extension.or(self + let file = self .buffer .read(cx) .as_singleton() - .and_then(|b| b.read(cx).file()) + .and_then(|b| b.read(cx).file()); + let file_extension = file_extension.or(file + .as_ref() .and_then(|file| Path::new(file.file_name(cx)).extension()) .and_then(|e| e.to_str()) .map(|a| a.to_string())); @@ -7091,7 +7093,8 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); let telemetry_settings = *settings::get::(cx); - let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None); + let copilot_enabled = + all_language_settings(file.map(|f| f.as_ref()), cx).copilot_enabled(None, None); let copilot_enabled_for_language = self .buffer .read(cx) diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 70aaca03c4..e4e283bd7e 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -26,8 +26,14 @@ pub fn language_settings<'a>( .language(language) } -pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings { - settings::get::(cx) +pub fn all_language_settings<'a>( + file: Option<&dyn File>, + cx: &'a AppContext, +) -> &'a AllLanguageSettings { + settings::get_local::( + file.map(|f| (f.worktree_id(), f.path().as_ref())), + cx, + ) } #[derive(Debug, Clone)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3cc4a181c5..c871bf0b96 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,7 +28,7 @@ use gpui::{ ModelHandle, Task, WeakModelHandle, }; use language::{ - language_settings::{all_language_settings, language_settings, FormatOnSave, Formatter}, + language_settings::{language_settings, FormatOnSave, Formatter}, point_to_lsp, proto::{ deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, @@ -689,18 +689,15 @@ impl Project { } fn on_settings_changed(&mut self, cx: &mut ModelContext) { - let settings = all_language_settings(cx); - let mut language_servers_to_start = Vec::new(); for buffer in self.opened_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { - if settings - .language(Some(&language.name())) - .enable_language_server - { + let settings = + language_settings(Some(language.name().as_ref()), Some(file), cx); + if settings.enable_language_server { language_servers_to_start.push((file.worktree.clone(), language.clone())); } } @@ -708,18 +705,22 @@ impl Project { } let mut language_servers_to_stop = Vec::new(); - for language in self.languages.to_vec() { - for lsp_adapter in language.lsp_adapters() { - if !settings - .language(Some(&language.name())) + let languages = self.languages.to_vec(); + for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { + let language = languages.iter().find(|l| { + l.lsp_adapters() + .iter() + .any(|adapter| &adapter.name == started_lsp_name) + }); + if let Some(language) = language { + let worktree = self.worktree_for_id(*worktree_id, cx); + let file = worktree.and_then(|tree| tree.update(cx, |tree, cx| tree.root_file(cx))); + // let settings = + // language_settings(Some(language.name().as_ref()), Some(file), cx); + if !language_settings(Some(&language.name()), file.as_ref().map(|f| f as _), cx) .enable_language_server { - let lsp_name = &lsp_adapter.name; - for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { - if lsp_name == started_lsp_name { - language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); - } - } + language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 7432eb8d40..5d121adc63 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -679,7 +679,7 @@ impl Worktree { } pub fn root_file(&self, cx: &mut ModelContext) -> Option { - let entry = self.entry_for_path("")?; + let entry = self.root_entry()?; Some(File { worktree: cx.handle(), path: entry.path.clone(), From 38078b93cc1361b58ef4529058ab3e62cc2dc3d3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:44:48 -0700 Subject: [PATCH 038/131] v0.90.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3751b34d31..2b340cf46a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8777,7 +8777,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.89.0" +version = "0.90.0" dependencies = [ "activity_indicator", "ai", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index e96dff1221..c45991cf11 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.89.0" +version = "0.90.0" publish = false [lib] From 03a351fb26a454c1dbd8eec93a4a05f50f4768e0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:57:04 -0700 Subject: [PATCH 039/131] Make language settings accessors take an arc dyn file --- crates/copilot/src/copilot.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 2 +- crates/editor/src/editor.rs | 5 ++-- crates/editor/src/multi_buffer.rs | 4 +-- crates/language/src/buffer.rs | 8 ++---- crates/language/src/language_settings.rs | 4 +-- crates/project/src/lsp_command.rs | 3 +-- crates/project/src/project.rs | 30 +++++++++------------ crates/project/src/project_tests.rs | 20 ++++++++------ crates/project/src/worktree.rs | 17 ++++-------- 10 files changed, 41 insertions(+), 54 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 1084222648..9343bd33f6 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -787,7 +787,7 @@ impl Copilot { let position = position.to_point_utf16(buffer); let settings = language_settings( buffer.language_at(position).map(|l| l.name()).as_deref(), - buffer.file().map(|f| f.as_ref()), + buffer.file(), cx, ); let tab_size = settings.tab_size; diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 4cb7c15bd9..27fd2dcb7c 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -285,7 +285,7 @@ impl CopilotButton { let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(self.file.as_ref().map(|f| f.as_ref()), cx).copilot_enabled( + all_language_settings(self.file.as_ref(), cx).copilot_enabled( language_name.as_deref(), file.as_ref().map(|file| file.path().as_ref()), ), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4a9237f563..1b62192999 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3211,7 +3211,7 @@ impl Editor { let language_name = snapshot .language_at(location) .map(|language| language.name()); - let settings = all_language_settings(file.map(|f| f.as_ref() as _), cx); + let settings = all_language_settings(file, cx); settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) } @@ -7093,8 +7093,7 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); let telemetry_settings = *settings::get::(cx); - let copilot_enabled = - all_language_settings(file.map(|f| f.as_ref()), cx).copilot_enabled(None, None); + let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None); let copilot_enabled_for_language = self .buffer .read(cx) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 597feb832b..1cf30a1676 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1382,7 +1382,7 @@ impl MultiBuffer { if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { let buffer = buffer.read(cx); language = buffer.language_at(offset).map(|l| l.name()); - file = buffer.file().map(|f| f.as_ref()); + file = buffer.file(); } language_settings(language.as_deref(), file, cx) } @@ -2795,7 +2795,7 @@ impl MultiBufferSnapshot { let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { language = buffer.language_at(offset).map(|l| l.name()); - file = buffer.file().map(|f| f.as_ref()); + file = buffer.file(); } language_settings(language.as_deref(), file, cx) } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index ddec085e2a..90a86d065c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1808,11 +1808,7 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings( - language_name.as_deref(), - self.file().map(|f| f.as_ref()), - cx, - ); + let settings = language_settings(language_name.as_deref(), self.file(), cx); if settings.hard_tabs { IndentSize::tab() } else { @@ -2139,7 +2135,7 @@ impl BufferSnapshot { let language = self.language_at(position); language_settings( language.map(|l| l.name()).as_deref(), - self.file.as_ref().map(AsRef::as_ref), + self.file.as_ref(), cx, ) } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index e4e283bd7e..31d819b449 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -16,7 +16,7 @@ pub fn init(cx: &mut AppContext) { pub fn language_settings<'a>( language: Option<&str>, - file: Option<&dyn File>, + file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { settings::get_local::( @@ -27,7 +27,7 @@ pub fn language_settings<'a>( } pub fn all_language_settings<'a>( - file: Option<&dyn File>, + file: Option<&Arc>, cx: &'a AppContext, ) -> &'a AllLanguageSettings { settings::get_local::( diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 0a8b5f8a2e..f349566535 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1717,8 +1717,7 @@ impl LspCommand for OnTypeFormatting { let tab_size = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - let file = buffer.file().map(|f| f.as_ref()); - language_settings(language_name.as_deref(), file, cx).tab_size + language_settings(language_name.as_deref(), buffer.file(), cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c871bf0b96..a106632e36 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -693,12 +693,14 @@ impl Project { for buffer in self.opened_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); - if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) - { + if let Some((file, language)) = buffer.file().zip(buffer.language()) { let settings = language_settings(Some(language.name().as_ref()), Some(file), cx); if settings.enable_language_server { - language_servers_to_start.push((file.worktree.clone(), language.clone())); + if let Some(file) = File::from_dyn(Some(file)) { + language_servers_to_start + .push((file.worktree.clone(), language.clone())); + } } } } @@ -714,10 +716,10 @@ impl Project { }); if let Some(language) = language { let worktree = self.worktree_for_id(*worktree_id, cx); - let file = worktree.and_then(|tree| tree.update(cx, |tree, cx| tree.root_file(cx))); - // let settings = - // language_settings(Some(language.name().as_ref()), Some(file), cx); - if !language_settings(Some(&language.name()), file.as_ref().map(|f| f as _), cx) + let file = worktree.and_then(|tree| { + tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _)) + }); + if !language_settings(Some(&language.name()), file.as_ref(), cx) .enable_language_server { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); @@ -2362,8 +2364,8 @@ impl Project { Some(&language.name()), worktree .update(cx, |tree, cx| tree.root_file(cx)) - .as_ref() - .map(|f| f as _), + .map(|f| f as _) + .as_ref(), cx, ) .enable_language_server @@ -3464,12 +3466,7 @@ impl Project { for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings( - language_name.as_deref(), - buffer.file().map(|f| f.as_ref()), - cx, - ) - .clone() + language_settings(language_name.as_deref(), buffer.file(), cx).clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4481,10 +4478,9 @@ impl Project { let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { let position = position.to_point_utf16(buffer); let language_name = buffer.language_at(position).map(|l| l.name()); - let file = buffer.file().map(|f| f.as_ref()); ( position, - language_settings(language_name.as_deref(), file, cx).tab_size, + language_settings(language_name.as_deref(), buffer.file(), cx).tab_size, ) }); self.request_lsp( diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 577b22d730..ee6752412d 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -99,18 +99,22 @@ async fn test_managing_project_specific_settings( let settings_a = language_settings( None, - Some(&File::for_entry( - tree.entry_for_path("a/a.rs").unwrap().clone(), - worktree.clone(), - )), + Some( + &(File::for_entry( + tree.entry_for_path("a/a.rs").unwrap().clone(), + worktree.clone(), + ) as _), + ), cx, ); let settings_b = language_settings( None, - Some(&File::for_entry( - tree.entry_for_path("b/b.rs").unwrap().clone(), - worktree.clone(), - )), + Some( + &(File::for_entry( + tree.entry_for_path("b/b.rs").unwrap().clone(), + worktree.clone(), + ) as _), + ), cx, ); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 5d121adc63..a33a0fc050 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -678,16 +678,9 @@ impl Worktree { } } - pub fn root_file(&self, cx: &mut ModelContext) -> Option { + pub fn root_file(&self, cx: &mut ModelContext) -> Option> { let entry = self.root_entry()?; - Some(File { - worktree: cx.handle(), - path: entry.path.clone(), - mtime: entry.mtime, - entry_id: entry.id, - is_local: self.is_local(), - is_deleted: false, - }) + Some(File::for_entry(entry.clone(), cx.handle())) } } @@ -2463,15 +2456,15 @@ impl language::LocalFile for File { } impl File { - pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Self { - Self { + pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Arc { + Arc::new(Self { worktree, path: entry.path.clone(), mtime: entry.mtime, entry_id: entry.id, is_local: true, is_deleted: false, - } + }) } pub fn from_proto( From 0dd7694ff531a5ae049fd754ddef5787a2a76e11 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:10:30 -0700 Subject: [PATCH 040/131] Make language_settings take a language, not a language name --- crates/copilot/src/copilot.rs | 6 +---- crates/copilot_button/src/copilot_button.rs | 27 +++++++++------------ crates/editor/src/display_map.rs | 7 +++--- crates/editor/src/editor.rs | 6 ++--- crates/editor/src/multi_buffer.rs | 8 +++--- crates/language/src/buffer.rs | 10 ++------ crates/language/src/language_settings.rs | 11 +++++---- crates/project/src/lsp_command.rs | 3 +-- crates/project/src/project.rs | 16 +++++------- crates/zed/src/languages/yaml.rs | 7 ++++-- 10 files changed, 41 insertions(+), 60 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 9343bd33f6..7834603552 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -785,11 +785,7 @@ impl Copilot { let buffer = buffer.read(cx); let uri = registered_buffer.uri.clone(); let position = position.to_point_utf16(buffer); - let settings = language_settings( - buffer.language_at(position).map(|l| l.name()).as_deref(), - buffer.file(), - cx, - ); + let settings = language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx); let tab_size = settings.tab_size; let hard_tabs = settings.hard_tabs; let relative_path = buffer diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 27fd2dcb7c..e34fddd9b9 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -11,7 +11,7 @@ use gpui::{ }; use language::{ language_settings::{self, all_language_settings, AllLanguageSettings}, - File, + File, Language, }; use settings::{update_settings_file, SettingsStore}; use std::{path::Path, sync::Arc}; @@ -29,7 +29,7 @@ pub struct CopilotButton { popup_menu: ViewHandle, editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, - language: Option>, + language: Option>, file: Option>, fs: Arc, } @@ -200,14 +200,13 @@ impl CopilotButton { if let Some(language) = self.language.clone() { let fs = fs.clone(); - let language_enabled = - language_settings::language_settings(Some(language.as_ref()), None, cx) - .show_copilot_suggestions; + let language_enabled = language_settings::language_settings(Some(&language), None, cx) + .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( "{} Suggestions for {}", if language_enabled { "Hide" } else { "Show" }, - language + language.name() ), move |cx| toggle_copilot_for_language(language.clone(), fs.clone(), cx), )); @@ -279,18 +278,14 @@ impl CopilotButton { let editor = editor.read(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let suggestion_anchor = editor.selections.newest_anchor().start; - let language_name = snapshot - .language_at(suggestion_anchor) - .map(|language| language.name()); + let language = snapshot.language_at(suggestion_anchor); let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(self.file.as_ref(), cx).copilot_enabled( - language_name.as_deref(), - file.as_ref().map(|file| file.path().as_ref()), - ), + all_language_settings(self.file.as_ref(), cx) + .copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())), ); - self.language = language_name; + self.language = language.cloned(); self.file = file; cx.notify() @@ -374,12 +369,12 @@ fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { }); } -fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { +fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(Some(&language), None); update_settings_file::(fs, cx, move |file| { file.languages - .entry(language) + .entry(language.name()) .or_default() .show_copilot_suggestions = Some(!show_copilot_suggestions); }); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index f8d6f0bc40..b0483db68d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -272,12 +272,11 @@ impl DisplayMap { } fn tab_size(buffer: &ModelHandle, cx: &mut ModelContext) -> NonZeroU32 { - let language_name = buffer + let language = buffer .read(cx) .as_singleton() - .and_then(|buffer| buffer.read(cx).language()) - .map(|language| language.name()); - language_settings(language_name.as_deref(), None, cx).tab_size + .and_then(|buffer| buffer.read(cx).language()); + language_settings(language.as_deref(), None, cx).tab_size } #[cfg(test)] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1b62192999..9a9f36c577 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3208,11 +3208,9 @@ impl Editor { cx: &mut ViewContext, ) -> bool { let file = snapshot.file_at(location); - let language_name = snapshot - .language_at(location) - .map(|language| language.name()); + let language = snapshot.language_at(location); let settings = all_language_settings(file, cx); - settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) + settings.copilot_enabled(language, file.map(|f| f.path().as_ref())) } fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 1cf30a1676..b7b2b89c8c 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1381,10 +1381,10 @@ impl MultiBuffer { let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { let buffer = buffer.read(cx); - language = buffer.language_at(offset).map(|l| l.name()); + language = buffer.language_at(offset); file = buffer.file(); } - language_settings(language.as_deref(), file, cx) + language_settings(language.as_ref(), file, cx) } pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { @@ -2794,10 +2794,10 @@ impl MultiBufferSnapshot { let mut language = None; let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { - language = buffer.language_at(offset).map(|l| l.name()); + language = buffer.language_at(offset); file = buffer.file(); } - language_settings(language.as_deref(), file, cx) + language_settings(language, file, cx) } pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 90a86d065c..7acb36a92f 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1807,8 +1807,7 @@ impl BufferSnapshot { } pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { - let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings(language_name.as_deref(), self.file(), cx); + let settings = language_settings(self.language_at(position), self.file(), cx); if settings.hard_tabs { IndentSize::tab() } else { @@ -2132,12 +2131,7 @@ impl BufferSnapshot { position: D, cx: &'a AppContext, ) -> &'a LanguageSettings { - let language = self.language_at(position); - language_settings( - language.map(|l| l.name()).as_deref(), - self.file.as_ref(), - cx, - ) + language_settings(self.language_at(position), self.file.as_ref(), cx) } pub fn language_scope_at(&self, position: D) -> Option { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 31d819b449..b03dd6a6b9 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -1,4 +1,4 @@ -use crate::File; +use crate::{File, Language}; use anyhow::Result; use collections::HashMap; use globset::GlobMatcher; @@ -15,7 +15,7 @@ pub fn init(cx: &mut AppContext) { } pub fn language_settings<'a>( - language: Option<&str>, + language: Option<&Arc>, file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { @@ -23,7 +23,7 @@ pub fn language_settings<'a>( file.map(|f| (f.worktree_id(), f.path().as_ref())), cx, ) - .language(language) + .language(language.map(|l| l.name()).as_deref()) } pub fn all_language_settings<'a>( @@ -170,7 +170,7 @@ impl AllLanguageSettings { .any(|glob| glob.is_match(path)) } - pub fn copilot_enabled(&self, language_name: Option<&str>, path: Option<&Path>) -> bool { + pub fn copilot_enabled(&self, language: Option<&Arc>, path: Option<&Path>) -> bool { if !self.copilot.feature_enabled { return false; } @@ -181,7 +181,8 @@ impl AllLanguageSettings { } } - self.language(language_name).show_copilot_suggestions + self.language(language.map(|l| l.name()).as_deref()) + .show_copilot_suggestions } } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index f349566535..7dab8eff30 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1716,8 +1716,7 @@ impl LspCommand for OnTypeFormatting { .await?; let tab_size = buffer.read_with(&cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), buffer.file(), cx).tab_size + language_settings(buffer.language(), buffer.file(), cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a106632e36..bec4338596 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -694,8 +694,7 @@ impl Project { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); if let Some((file, language)) = buffer.file().zip(buffer.language()) { - let settings = - language_settings(Some(language.name().as_ref()), Some(file), cx); + let settings = language_settings(Some(language), Some(file), cx); if settings.enable_language_server { if let Some(file) = File::from_dyn(Some(file)) { language_servers_to_start @@ -719,9 +718,7 @@ impl Project { let file = worktree.and_then(|tree| { tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _)) }); - if !language_settings(Some(&language.name()), file.as_ref(), cx) - .enable_language_server - { + if !language_settings(Some(language), file.as_ref(), cx).enable_language_server { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } } @@ -2361,7 +2358,7 @@ impl Project { cx: &mut ModelContext, ) { if !language_settings( - Some(&language.name()), + Some(&language), worktree .update(cx, |tree, cx| tree.root_file(cx)) .map(|f| f as _) @@ -3465,8 +3462,7 @@ impl Project { let mut project_transaction = ProjectTransaction::default(); for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), buffer.file(), cx).clone() + language_settings(buffer.language(), buffer.file(), cx).clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4477,10 +4473,10 @@ impl Project { ) -> Task>> { let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { let position = position.to_point_utf16(buffer); - let language_name = buffer.language_at(position).map(|l| l.name()); ( position, - language_settings(language_name.as_deref(), buffer.file(), cx).tab_size, + language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx) + .tab_size, ) }); self.request_lsp( diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index d66602ee04..7f87a7caed 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; use language::{ - language_settings::language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, + language_settings::all_language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, }; use node_runtime::NodeRuntime; use serde_json::Value; @@ -101,13 +101,16 @@ impl LspAdapter for YamlLspAdapter { } fn workspace_configuration(&self, cx: &mut AppContext) -> Option> { + let tab_size = all_language_settings(None, cx) + .language(Some("YAML")) + .tab_size; Some( future::ready(serde_json::json!({ "yaml": { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": language_settings(Some("YAML"), None, cx).tab_size, + "editor.tabSize": tab_size, } })) .boxed(), From 0d281c1b89531547879e666320728a47336b47a9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:13:01 -0700 Subject: [PATCH 041/131] :art: --- crates/language/src/language_settings.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index b03dd6a6b9..332e789b4d 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -19,21 +19,16 @@ pub fn language_settings<'a>( file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { - settings::get_local::( - file.map(|f| (f.worktree_id(), f.path().as_ref())), - cx, - ) - .language(language.map(|l| l.name()).as_deref()) + let language_name = language.map(|l| l.name()); + all_language_settings(file, cx).language(language_name.as_deref()) } pub fn all_language_settings<'a>( file: Option<&Arc>, cx: &'a AppContext, ) -> &'a AllLanguageSettings { - settings::get_local::( - file.map(|f| (f.worktree_id(), f.path().as_ref())), - cx, - ) + let location = file.map(|f| (f.worktree_id(), f.path().as_ref())); + settings::get_local(location, cx) } #[derive(Debug, Clone)] From ae7606ce21d089ab10b751fc1a44720fe1592841 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:38:26 -0700 Subject: [PATCH 042/131] collab 0.12.5 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b340cf46a..07a859b058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1246,7 +1246,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.12.4" +version = "0.12.5" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index cd06b9a70a..07d2cdc60a 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.12.4" +version = "0.12.5" publish = false [[bin]] From e1a6dc90770ad5269ad5d464606028a6dadfe160 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:03:02 +0200 Subject: [PATCH 043/131] search: Add a default tooltip for empty searches Z-1074 --- crates/search/src/project_search.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 2c22517e20..0f8c9173bc 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -25,7 +25,7 @@ use std::{ borrow::Cow, collections::HashSet, mem, - ops::Range, + ops::{Not, Range}, path::PathBuf, sync::Arc, }; @@ -242,7 +242,13 @@ impl View for ProjectSearchView { impl Item for ProjectSearchView { fn tab_tooltip_text(&self, cx: &AppContext) -> Option> { - Some(self.query_editor.read(cx).text(cx).into()) + let query_text = self.query_editor.read(cx).text(cx); + + query_text + .is_empty() + .not() + .then(|| query_text.into()) + .or_else(|| Some("Project search".into())) } fn act_as_type<'a>( From 01621972c5d05c5f377487a1dcf8b39bb3977969 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:31:37 +0200 Subject: [PATCH 044/131] Change "Project search" to "Project Search" --- crates/search/src/project_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0f8c9173bc..27aac1762b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -248,7 +248,7 @@ impl Item for ProjectSearchView { .is_empty() .not() .then(|| query_text.into()) - .or_else(|| Some("Project search".into())) + .or_else(|| Some("Project Search".into())) } fn act_as_type<'a>( From d8ce333cf8e347e25e9af9aca3a918e57da9c8af Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 1 Jun 2023 16:51:29 +0300 Subject: [PATCH 045/131] Remove wrong assertion --- crates/workspace/src/workspace.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 364c3cadaf..21feea2b40 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -974,9 +974,8 @@ impl Workspace { let timestamp = entry.timestamp; match history.entry(project_path) { hash_map::Entry::Occupied(mut entry) => { - let (old_fs_path, old_timestamp) = entry.get(); + let (_, old_timestamp) = entry.get(); if ×tamp > old_timestamp { - assert_eq!(&fs_path, old_fs_path, "Inconsistent nav history"); entry.insert((fs_path, timestamp)); } } From 51c82da8401219cbf8e7487bfd8fd381ca88082c Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 1 Jun 2023 12:04:45 -0400 Subject: [PATCH 046/131] Avoid blocking forever on startup if config files do not exist The files will still get created if the user opens their settings and saves, otherwise everything will transparently work Co-Authored-By: Antonio Scandurra Co-Authored-By: Max Brunsfeld --- crates/settings/src/settings_file.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 4c98dca51a..3505330eda 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -66,15 +66,22 @@ pub fn watch_config_file( .spawn(async move { let events = fs.watch(&path, Duration::from_millis(100)).await; futures::pin_mut!(events); + + let contents = fs.load(&path).await.unwrap_or_default(); + if tx.unbounded_send(contents).is_err() { + return; + } + loop { + if events.next().await.is_none() { + break; + } + if let Ok(contents) = fs.load(&path).await { if !tx.unbounded_send(contents).is_ok() { break; } } - if events.next().await.is_none() { - break; - } } }) .detach(); From 2390815d67a5b30763a7e3a1fa3f53836347e6ef Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 1 Jun 2023 10:09:04 -0700 Subject: [PATCH 047/131] Make settings store handle no user settings co-authored-by: max --- crates/settings/src/settings_store.rs | 193 +++++++++++++------------- 1 file changed, 97 insertions(+), 96 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index bb51fd1ed0..3fdb81dd45 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use collections::{btree_map, hash_map, BTreeMap, HashMap}; use gpui::AppContext; use lazy_static::lazy_static; @@ -84,15 +84,26 @@ pub struct SettingsJsonSchemaParams<'a> { } /// A set of strongly-typed setting values defined via multiple JSON files. -#[derive(Default)] pub struct SettingsStore { setting_values: HashMap>, - default_deserialized_settings: Option, - user_deserialized_settings: Option, + default_deserialized_settings: serde_json::Value, + user_deserialized_settings: serde_json::Value, local_deserialized_settings: BTreeMap<(usize, Arc), serde_json::Value>, tab_size_callback: Option<(TypeId, Box Option>)>, } +impl Default for SettingsStore { + fn default() -> Self { + SettingsStore { + setting_values: Default::default(), + default_deserialized_settings: serde_json::json!({}), + user_deserialized_settings: serde_json::json!({}), + local_deserialized_settings: Default::default(), + tab_size_callback: Default::default(), + } + } +} + #[derive(Debug)] struct SettingValue { global_value: Option, @@ -136,27 +147,24 @@ impl SettingsStore { local_values: Vec::new(), })); - if let Some(default_settings) = &self.default_deserialized_settings { - if let Some(default_settings) = setting_value - .deserialize_setting(default_settings) + if let Some(default_settings) = setting_value + .deserialize_setting(&self.default_deserialized_settings) + .log_err() + { + let mut user_values_stack = Vec::new(); + + if let Some(user_settings) = setting_value + .deserialize_setting(&self.user_deserialized_settings) .log_err() { - let mut user_values_stack = Vec::new(); + user_values_stack = vec![user_settings]; + } - if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_settings) = - setting_value.deserialize_setting(user_settings).log_err() - { - user_values_stack = vec![user_settings]; - } - } - - if let Some(setting) = setting_value - .load_setting(&default_settings, &user_values_stack, cx) - .log_err() - { - setting_value.set_global_value(setting); - } + if let Some(setting) = setting_value + .load_setting(&default_settings, &user_values_stack, cx) + .log_err() + { + setting_value.set_global_value(setting); } } } @@ -189,9 +197,7 @@ impl SettingsStore { /// This is only for debugging and reporting. For user-facing functionality, /// use the typed setting interface. pub fn untyped_user_settings(&self) -> &serde_json::Value { - self.user_deserialized_settings - .as_ref() - .unwrap_or(&serde_json::Value::Null) + &self.user_deserialized_settings } #[cfg(any(test, feature = "test-support"))] @@ -213,11 +219,7 @@ impl SettingsStore { cx: &AppContext, update: impl FnOnce(&mut T::FileContent), ) { - if self.user_deserialized_settings.is_none() { - self.set_user_settings("{}", cx).unwrap(); - } - let old_text = - serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap(); + let old_text = serde_json::to_string(&self.user_deserialized_settings).unwrap(); let new_text = self.new_text_for_update::(old_text, update); self.set_user_settings(&new_text, cx).unwrap(); } @@ -250,11 +252,7 @@ impl SettingsStore { .setting_values .get(&setting_type_id) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) - .deserialize_setting( - self.user_deserialized_settings - .as_ref() - .expect("no user settings loaded"), - ) + .deserialize_setting(&self.user_deserialized_settings) .unwrap_or_else(|e| { panic!( "could not deserialize setting type {} from user settings: {}", @@ -323,10 +321,14 @@ impl SettingsStore { default_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.default_deserialized_settings = - Some(parse_json_with_comments(default_settings_content)?); - self.recompute_values(None, cx)?; - Ok(()) + let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?; + if settings.is_object() { + self.default_deserialized_settings = settings; + self.recompute_values(None, cx)?; + Ok(()) + } else { + Err(anyhow!("settings must be an object")) + } } /// Set the user settings via a JSON string. @@ -335,9 +337,14 @@ impl SettingsStore { user_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.user_deserialized_settings = Some(parse_json_with_comments(user_settings_content)?); - self.recompute_values(None, cx)?; - Ok(()) + let settings: serde_json::Value = parse_json_with_comments(user_settings_content)?; + if settings.is_object() { + self.user_deserialized_settings = settings; + self.recompute_values(None, cx)?; + Ok(()) + } else { + Err(anyhow!("settings must be an object")) + } } /// Add or remove a set of local settings via a JSON string. @@ -361,7 +368,6 @@ impl SettingsStore { /// Add or remove a set of local settings via a JSON string. pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> { - eprintln!("clearing local settings {root_id}"); self.local_deserialized_settings .retain(|k, _| k.0 != root_id); self.recompute_values(Some((root_id, "".as_ref())), cx)?; @@ -460,68 +466,63 @@ impl SettingsStore { let mut user_settings_stack = Vec::::new(); let mut paths_stack = Vec::>::new(); for setting_value in self.setting_values.values_mut() { - if let Some(default_settings) = &self.default_deserialized_settings { - let default_settings = setting_value.deserialize_setting(default_settings)?; + let default_settings = + setting_value.deserialize_setting(&self.default_deserialized_settings)?; - user_settings_stack.clear(); - paths_stack.clear(); + user_settings_stack.clear(); + paths_stack.clear(); - if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_settings) = - setting_value.deserialize_setting(user_settings).log_err() - { - user_settings_stack.push(user_settings); - paths_stack.push(None); + if let Some(user_settings) = setting_value + .deserialize_setting(&self.user_deserialized_settings) + .log_err() + { + user_settings_stack.push(user_settings); + paths_stack.push(None); + } + + // If the global settings file changed, reload the global value for the field. + if changed_local_path.is_none() { + if let Some(value) = setting_value + .load_setting(&default_settings, &user_settings_stack, cx) + .log_err() + { + setting_value.set_global_value(value); + } + } + + // Reload the local values for the setting. + for ((root_id, path), local_settings) in &self.local_deserialized_settings { + // Build a stack of all of the local values for that setting. + while let Some(prev_entry) = paths_stack.last() { + if let Some((prev_root_id, prev_path)) = prev_entry { + if root_id != prev_root_id || !path.starts_with(prev_path) { + paths_stack.pop(); + user_settings_stack.pop(); + continue; + } } + break; } - // If the global settings file changed, reload the global value for the field. - if changed_local_path.is_none() { + if let Some(local_settings) = + setting_value.deserialize_setting(&local_settings).log_err() + { + paths_stack.push(Some((*root_id, path.as_ref()))); + user_settings_stack.push(local_settings); + + // If a local settings file changed, then avoid recomputing local + // settings for any path outside of that directory. + if changed_local_path.map_or(false, |(changed_root_id, changed_local_path)| { + *root_id != changed_root_id || !path.starts_with(changed_local_path) + }) { + continue; + } + if let Some(value) = setting_value .load_setting(&default_settings, &user_settings_stack, cx) .log_err() { - setting_value.set_global_value(value); - } - } - - // Reload the local values for the setting. - for ((root_id, path), local_settings) in &self.local_deserialized_settings { - // Build a stack of all of the local values for that setting. - while let Some(prev_entry) = paths_stack.last() { - if let Some((prev_root_id, prev_path)) = prev_entry { - if root_id != prev_root_id || !path.starts_with(prev_path) { - paths_stack.pop(); - user_settings_stack.pop(); - continue; - } - } - break; - } - - if let Some(local_settings) = - setting_value.deserialize_setting(&local_settings).log_err() - { - paths_stack.push(Some((*root_id, path.as_ref()))); - user_settings_stack.push(local_settings); - - // If a local settings file changed, then avoid recomputing local - // settings for any path outside of that directory. - if changed_local_path.map_or( - false, - |(changed_root_id, changed_local_path)| { - *root_id != changed_root_id || !path.starts_with(changed_local_path) - }, - ) { - continue; - } - - if let Some(value) = setting_value - .load_setting(&default_settings, &user_settings_stack, cx) - .log_err() - { - setting_value.set_local_value(*root_id, path.clone(), value); - } + setting_value.set_local_value(*root_id, path.clone(), value); } } } From 40c6baf7cbd92bbe700c57e18d7bc442fe12eb71 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 1 Jun 2023 10:49:27 -0700 Subject: [PATCH 048/131] Redraw the terminal on every wakeup co-authored-by: max --- crates/terminal_view/src/terminal_view.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 3c9836fb75..9478b3eef1 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -133,8 +133,8 @@ impl TerminalView { Event::Wakeup => { if !cx.is_self_focused() { this.has_new_content = true; - cx.notify(); } + cx.notify(); cx.emit(Event::Wakeup); } Event::Bell => { From 0065f5715ceaa905b5fa6ba07a2ab9ec923498c7 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Thu, 1 Jun 2023 16:40:54 -0400 Subject: [PATCH 049/131] Add panic events --- crates/zed/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 31f331ef93..9ec3e3d3f6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -41,7 +41,7 @@ use std::{ Arc, Weak, }, thread, - time::Duration, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use sum_tree::Bias; use terminal_view::{get_working_directory, TerminalSettings, TerminalView}; @@ -376,6 +376,7 @@ struct Panic { backtrace: Vec, // TODO // stripped_backtrace: String, + time: u128, } #[derive(Serialize)] @@ -413,6 +414,10 @@ fn init_panic_hook(app_version: String) { .map(|line| line.to_string()) .collect(), // modified_backtrace: None, + time: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(), }; if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() { From e67e6e6f70b76907d5e44628117a7cc48d0616c9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 1 Jun 2023 14:07:59 -0700 Subject: [PATCH 050/131] Fix ambiguous glob export warnings in rust 1.70 --- crates/editor/src/editor.rs | 4 +++- crates/gpui/src/elements/uniform_list.rs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index baf2f062fe..5c19bb0121 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -31,7 +31,9 @@ use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; pub use editor_settings::EditorSettings; -pub use element::*; +pub use element::{ + Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles, +}; use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 9ccd57b291..8344914da0 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -36,7 +36,7 @@ struct StateInner { scroll_to: Option, } -pub struct LayoutState { +pub struct UniformListLayoutState { scroll_max: f32, item_height: f32, items: Vec>, @@ -152,7 +152,7 @@ impl UniformList { } impl Element for UniformList { - type LayoutState = LayoutState; + type LayoutState = UniformListLayoutState; type PaintState = (); fn layout( @@ -169,7 +169,7 @@ impl Element for UniformList { let no_items = ( constraint.min, - LayoutState { + UniformListLayoutState { item_height: 0., scroll_max: 0., items: Default::default(), @@ -263,7 +263,7 @@ impl Element for UniformList { ( size, - LayoutState { + UniformListLayoutState { item_height, scroll_max, items, From 585d13d3db5e589174c89fa141493b2d2196076b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 1 Jun 2023 15:20:15 -0700 Subject: [PATCH 051/131] Unzoom all panes when opening a dock --- crates/workspace/src/workspace.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 21feea2b40..795747cfcc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1705,7 +1705,7 @@ impl Workspace { ) { // If a center pane is zoomed, unzoom it. for pane in &self.panes { - if pane != &self.active_pane { + if pane != &self.active_pane || dock_to_reveal.is_some() { pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); } } @@ -4351,6 +4351,12 @@ mod tests { panel }); + let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + pane.update(cx, |pane, cx| { + let item = cx.add_view(|_| TestItem::new()); + pane.add_item(Box::new(item), true, true, None, cx); + }); + // Transfer focus from center to panel workspace.update(cx, |workspace, cx| { workspace.toggle_panel_focus::(cx); @@ -4452,6 +4458,25 @@ mod tests { assert!(workspace.zoomed.is_some()); assert!(panel.has_focus(cx)); }); + + // Unzoom and close the panel, zoom the active pane. + panel.update(cx, |panel, cx| panel.set_zoomed(false, cx)); + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx)); + + // Opening a dock unzooms the pane. + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + workspace.read_with(cx, |workspace, cx| { + let pane = pane.read(cx); + assert!(!pane.is_zoomed()); + assert!(pane.has_focus()); + assert!(workspace.right_dock().read(cx).is_open()); + assert!(workspace.zoomed.is_none()); + }); } #[gpui::test] From cc055901e1b530c80a73eb5f98ba1572064c77bd Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Thu, 1 Jun 2023 23:34:39 -0400 Subject: [PATCH 052/131] Add additional panic information to panic events --- crates/feedback/src/system_specs.rs | 2 ++ crates/zed/src/main.rs | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/crates/feedback/src/system_specs.rs b/crates/feedback/src/system_specs.rs index b6eb6e8b6d..48955a8629 100644 --- a/crates/feedback/src/system_specs.rs +++ b/crates/feedback/src/system_specs.rs @@ -6,6 +6,8 @@ use std::{env, fmt::Display}; use sysinfo::{System, SystemExt}; use util::channel::ReleaseChannel; +// TODO: Move this file out of feedback and into a more general place + #[derive(Clone, Debug, Serialize)] pub struct SystemSpecs { #[serde(serialize_with = "serialize_app_version")] diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 9ec3e3d3f6..4b0661c6e6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -70,10 +70,7 @@ fn main() { log::info!("========== starting zed =========="); let mut app = gpui::App::new(Assets).unwrap(); - let app_version = ZED_APP_VERSION - .or_else(|| app.platform().app_version().ok()) - .map_or("dev".to_string(), |v| v.to_string()); - init_panic_hook(app_version); + init_panic_hook(&app); app.background(); @@ -376,19 +373,29 @@ struct Panic { backtrace: Vec, // TODO // stripped_backtrace: String, + release_channel: String, + os_name: String, + os_version: Option, + architecture: String, time: u128, } #[derive(Serialize)] struct PanicRequest { panic: Panic, + // TODO: Move to Panic struct, as app_version - requires changing zed.dev version: String, token: String, } -fn init_panic_hook(app_version: String) { +fn init_panic_hook(app: &App) { let is_pty = stdout_is_a_pty(); + let platform = app.platform(); + panic::set_hook(Box::new(move |info| { + let app_version = ZED_APP_VERSION + .or_else(|| platform.app_version().ok()) + .map_or("dev".to_string(), |v| v.to_string()); let backtrace = Backtrace::new(); let thread = thread::current(); @@ -414,6 +421,13 @@ fn init_panic_hook(app_version: String) { .map(|line| line.to_string()) .collect(), // modified_backtrace: None, + release_channel: RELEASE_CHANNEL.dev_name().into(), + os_name: platform.os_name().into(), + os_version: platform + .os_version() + .ok() + .map(|os_version| os_version.to_string()), + architecture: env::consts::ARCH.into(), time: SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() From c55aee84d3fcd6d9a4a073168825262dba8d4b73 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Fri, 2 Jun 2023 01:33:25 -0400 Subject: [PATCH 053/131] Rename field to panicked_on --- crates/zed/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 4b0661c6e6..e22edee5f6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -377,7 +377,7 @@ struct Panic { os_name: String, os_version: Option, architecture: String, - time: u128, + panicked_on: u128, } #[derive(Serialize)] @@ -428,7 +428,7 @@ fn init_panic_hook(app: &App) { .ok() .map(|os_version| os_version.to_string()), architecture: env::consts::ARCH.into(), - time: SystemTime::now() + panicked_on: SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis(), From cf934ab696ee69b05403e71445a86b2c03992f5e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 10:08:06 +0200 Subject: [PATCH 054/131] Fix compile errors --- assets/keymaps/default.json | 6 +++--- crates/ai/src/assistant.rs | 7 +++---- crates/editor/src/editor.rs | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index f58e3c7cce..41087326d2 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -185,7 +185,8 @@ ], "alt-\\": "copilot::Suggest", "alt-]": "copilot::NextSuggestion", - "alt-[": "copilot::PreviousSuggestion" + "alt-[": "copilot::PreviousSuggestion", + "cmd->": "assistant::QuoteSelection" } }, { @@ -199,8 +200,7 @@ "context": "ContextEditor > Editor", "bindings": { "cmd-enter": "assistant::Assist", - "escape": "assistant::CancelLastAssist", - "cmd-?": "assistant::QuoteSelection" + "cmd->": "assistant::QuoteSelection" } }, { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 6e76e46c61..38b64587a5 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -19,10 +19,7 @@ use workspace::{ pane, Pane, Workspace, }; -actions!( - assistant, - [NewContext, Assist, CancelLastAssist, QuoteSelection] -); +actions!(assistant, [NewContext, Assist, QuoteSelection]); pub fn init(cx: &mut AppContext) { cx.add_action(AssistantEditor::assist); @@ -69,6 +66,7 @@ impl AssistantPanel { .with_child(Pane::render_tab_bar_button( 0, "icons/plus_12.svg", + false, Some(("New Context".into(), Some(Box::new(NewContext)))), cx, move |_, _| todo!(), @@ -81,6 +79,7 @@ impl AssistantPanel { } else { "icons/maximize_8.svg" }, + pane.is_zoomed(), Some(( "Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a3601f3c58..ee06443068 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -31,6 +31,7 @@ use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; pub use editor_settings::EditorSettings; +pub use element::RenderExcerptHeaderParams; pub use element::{ Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles, }; From 55c8c6d3fbf1ba5ad5b4abf9d7e3b34ef2b6e7ba Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 10:29:22 +0200 Subject: [PATCH 055/131] Allow adding new contexts --- crates/ai/src/ai.rs | 7 +++--- crates/ai/src/assistant.rs | 48 +++++++++++++++++++++++++++----------- crates/zed/src/zed.rs | 7 ++++++ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 89999f26f3..fe1125bb98 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,9 +1,8 @@ -mod assistant; - -use gpui::{actions, AppContext}; -use serde::{Deserialize, Serialize}; +pub mod assistant; pub use assistant::AssistantPanel; +use gpui::{actions, AppContext}; +use serde::{Deserialize, Serialize}; actions!(ai, [Assist]); diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 38b64587a5..6f5cbd6416 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -19,9 +19,18 @@ use workspace::{ pane, Pane, Workspace, }; -actions!(assistant, [NewContext, Assist, QuoteSelection]); +actions!(assistant, [NewContext, Assist, QuoteSelection, ToggleFocus]); pub fn init(cx: &mut AppContext) { + cx.add_action( + |workspace: &mut Workspace, _: &NewContext, cx: &mut ViewContext| { + if let Some(this) = workspace.panel::(cx) { + this.update(cx, |this, cx| this.add_context(cx)) + } + + workspace.focus_panel::(cx); + }, + ); cx.add_action(AssistantEditor::assist); cx.capture_action(AssistantEditor::cancel_last_assist); cx.add_action(AssistantEditor::quote_selection); @@ -49,7 +58,8 @@ impl AssistantPanel { cx.spawn(|mut cx| async move { // TODO: deserialize state. workspace.update(&mut cx, |workspace, cx| { - cx.add_view(|cx| { + cx.add_view::(|cx| { + let weak_self = cx.weak_handle(); let pane = cx.add_view(|cx| { let mut pane = Pane::new( workspace.weak_handle(), @@ -62,6 +72,7 @@ impl AssistantPanel { pane.set_can_navigate(false, cx); pane.on_can_drop(move |_, _| false); pane.set_render_tab_bar_buttons(cx, move |pane, cx| { + let weak_self = weak_self.clone(); Flex::row() .with_child(Pane::render_tab_bar_button( 0, @@ -69,7 +80,14 @@ impl AssistantPanel { false, Some(("New Context".into(), Some(Box::new(NewContext)))), cx, - move |_, _| todo!(), + move |_, cx| { + let weak_self = weak_self.clone(); + cx.window_context().defer(move |cx| { + if let Some(this) = weak_self.upgrade(cx) { + this.update(cx, |this, cx| this.add_context(cx)); + } + }) + }, None, )) .with_child(Pane::render_tab_bar_button( @@ -125,6 +143,14 @@ impl AssistantPanel { _ => {} } } + + fn add_context(&mut self, cx: &mut ViewContext) { + let focus = self.has_focus(cx); + let editor = cx.add_view(|cx| AssistantEditor::new(self.languages.clone(), cx)); + self.pane.update(cx, |pane, cx| { + pane.add_item(Box::new(editor), true, focus, None, cx) + }); + } } impl Entity for AssistantPanel { @@ -187,11 +213,7 @@ impl Panel for AssistantPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { if active && self.pane.read(cx).items_len() == 0 { - let focus = self.has_focus(cx); - let editor = cx.add_view(|cx| AssistantEditor::new(self.languages.clone(), cx)); - self.pane.update(cx, |pane, cx| { - pane.add_item(Box::new(editor), true, focus, None, cx) - }); + self.add_context(cx); } } @@ -200,7 +222,7 @@ impl Panel for AssistantPanel { } fn icon_tooltip(&self) -> (String, Option>) { - ("Assistant Panel".into(), None) + ("Assistant Panel".into(), Some(Box::new(ToggleFocus))) } fn should_change_position_on_event(_: &Self::Event) -> bool { @@ -231,7 +253,7 @@ struct Assistant { messages_by_id: HashMap, completion_count: usize, pending_completions: Vec, - language_registry: Arc, + languages: Arc, } impl Entity for Assistant { @@ -246,7 +268,7 @@ impl Assistant { messages_by_id: Default::default(), completion_count: Default::default(), pending_completions: Default::default(), - language_registry, + languages: language_registry, }; this.push_message(Role::User, cx); this @@ -310,7 +332,7 @@ impl Assistant { fn push_message(&mut self, role: Role, cx: &mut ModelContext) -> Message { let content = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx); - let markdown = self.language_registry.language_for_name("Markdown"); + let markdown = self.languages.language_for_name("Markdown"); cx.spawn_weak(|buffer, mut cx| async move { let markdown = markdown.await?; let buffer = buffer @@ -322,7 +344,7 @@ impl Assistant { anyhow::Ok(()) }) .detach_and_log_err(cx); - buffer.set_language_registry(self.language_registry.clone()); + buffer.set_language_registry(self.languages.clone()); buffer }); let excerpt_id = self.buffer.update(cx, |buffer, cx| { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 8379251485..38e7a20d31 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -235,6 +235,13 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { workspace.toggle_panel_focus::(cx); }, ); + cx.add_action( + |workspace: &mut Workspace, + _: &ai::assistant::ToggleFocus, + cx: &mut ViewContext| { + workspace.toggle_panel_focus::(cx); + }, + ); cx.add_global_action({ let app_state = Arc::downgrade(&app_state); move |_: &NewWindow, cx: &mut AppContext| { From d0aff65b1cb9fe841aed9df8673e50f3deb96dc1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 10:55:19 +0200 Subject: [PATCH 056/131] Allow moving the assistant panel to other docks --- Cargo.lock | 3 + assets/settings/default.json | 10 +++ crates/ai/Cargo.toml | 3 + crates/ai/src/ai.rs | 1 + crates/ai/src/assistant.rs | 98 ++++++++++++++++++++++------- crates/ai/src/assistant_settings.rs | 42 +++++++++++++ crates/terminal_view/Cargo.toml | 2 - 7 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 crates/ai/src/assistant_settings.rs diff --git a/Cargo.lock b/Cargo.lock index 9634cd2c8e..4e3ded5bb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,13 +104,16 @@ dependencies = [ "chrono", "collections", "editor", + "fs", "futures 0.3.28", "gpui", "isahc", "language", + "schemars", "search", "serde", "serde_json", + "settings", "theme", "util", "workspace", diff --git a/assets/settings/default.json b/assets/settings/default.json index 23599c8dfb..695061a0aa 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -81,6 +81,16 @@ // 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": 480, + // Default height when the assistant is docked to the bottom. + "default_height": 320, + // OpenAI API key. + "openai_api_key": null + }, // Whether the screen sharing icon is shown in the os status bar. "show_call_status_icon": true, // Whether to use language servers to provide code intelligence. diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 861a9e4785..ce2a3338eb 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -12,9 +12,11 @@ doctest = false assets = { path = "../assets"} collections = { path = "../collections"} editor = { path = "../editor" } +fs = { path = "../fs" } gpui = { path = "../gpui" } language = { path = "../language" } search = { path = "../search" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } @@ -23,6 +25,7 @@ anyhow.workspace = true chrono = "0.4" futures.workspace = true isahc.workspace = true +schemars.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index fe1125bb98..a5d5666a72 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,4 +1,5 @@ pub mod assistant; +mod assistant_settings; pub use assistant::AssistantPanel; use gpui::{actions, AppContext}; diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 6f5cbd6416..ecc27538ed 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,8 +1,12 @@ -use crate::{OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role}; +use crate::{ + assistant_settings::{AssistantDockPosition, AssistantSettings}, + OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role, +}; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; use collections::HashMap; use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer}; +use fs::Fs; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ actions, elements::*, executor::Background, Action, AppContext, AsyncAppContext, Entity, @@ -11,6 +15,7 @@ use gpui::{ }; use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; +use settings::SettingsStore; use std::{io, sync::Arc}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ @@ -22,6 +27,7 @@ use workspace::{ actions!(assistant, [NewContext, Assist, QuoteSelection, ToggleFocus]); pub fn init(cx: &mut AppContext) { + settings::register::(cx); cx.add_action( |workspace: &mut Workspace, _: &NewContext, cx: &mut ViewContext| { if let Some(this) = workspace.panel::(cx) { @@ -41,12 +47,15 @@ pub enum AssistantPanelEvent { ZoomOut, Focus, Close, + DockPositionChanged, } pub struct AssistantPanel { width: Option, + height: Option, pane: ViewHandle, languages: Arc, + fs: Arc, _subscriptions: Vec, } @@ -113,17 +122,40 @@ impl AssistantPanel { .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); pane }); - let subscriptions = vec![ - cx.observe(&pane, |_, _, cx| cx.notify()), - cx.subscribe(&pane, Self::handle_pane_event), - ]; - - Self { + let mut this = Self { pane, languages: workspace.app_state().languages.clone(), + fs: workspace.app_state().fs.clone(), width: None, - _subscriptions: subscriptions, - } + height: None, + _subscriptions: Default::default(), + }; + + let mut old_dock_position = this.position(cx); + let mut old_openai_api_key = settings::get::(cx) + .openai_api_key + .clone(); + this._subscriptions = vec![ + cx.observe(&this.pane, |_, _, cx| cx.notify()), + cx.subscribe(&this.pane, Self::handle_pane_event), + cx.observe_global::(move |this, cx| { + let new_dock_position = this.position(cx); + if new_dock_position != old_dock_position { + old_dock_position = new_dock_position; + cx.emit(AssistantPanelEvent::DockPositionChanged); + } + + let new_openai_api_key = settings::get::(cx) + .openai_api_key + .clone(); + if old_openai_api_key != new_openai_api_key { + old_openai_api_key = new_openai_api_key; + cx.notify(); + } + }), + ]; + + this }) }) }) @@ -174,24 +206,44 @@ impl View for AssistantPanel { } impl Panel for AssistantPanel { - fn position(&self, _: &WindowContext) -> DockPosition { - DockPosition::Right + fn position(&self, cx: &WindowContext) -> DockPosition { + match settings::get::(cx).dock { + AssistantDockPosition::Left => DockPosition::Left, + AssistantDockPosition::Bottom => DockPosition::Bottom, + AssistantDockPosition::Right => DockPosition::Right, + } } - fn position_is_valid(&self, position: DockPosition) -> bool { - matches!(position, DockPosition::Right) + fn position_is_valid(&self, _: DockPosition) -> bool { + true } - fn set_position(&mut self, _: DockPosition, _: &mut ViewContext) { - // TODO! + fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) { + settings::update_settings_file::(self.fs.clone(), cx, move |settings| { + let dock = match position { + DockPosition::Left => AssistantDockPosition::Left, + DockPosition::Bottom => AssistantDockPosition::Bottom, + DockPosition::Right => AssistantDockPosition::Right, + }; + settings.dock = Some(dock); + }); } - fn size(&self, _: &WindowContext) -> f32 { - self.width.unwrap_or(480.) + fn size(&self, cx: &WindowContext) -> f32 { + let settings = settings::get::(cx); + match self.position(cx) { + DockPosition::Left | DockPosition::Right => { + self.width.unwrap_or_else(|| settings.default_width) + } + DockPosition::Bottom => self.height.unwrap_or_else(|| settings.default_height), + } } fn set_size(&mut self, size: f32, cx: &mut ViewContext) { - self.width = Some(size); + match self.position(cx) { + DockPosition::Left | DockPosition::Right => self.width = Some(size), + DockPosition::Bottom => self.height = Some(size), + } cx.notify(); } @@ -225,9 +277,8 @@ impl Panel for AssistantPanel { ("Assistant Panel".into(), Some(Box::new(ToggleFocus))) } - fn should_change_position_on_event(_: &Self::Event) -> bool { - // TODO! - false + fn should_change_position_on_event(event: &Self::Event) -> bool { + matches!(event, AssistantPanelEvent::DockPositionChanged) } fn should_activate_on_event(_: &Self::Event) -> bool { @@ -289,7 +340,10 @@ impl Assistant { stream: true, }; - if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() { + if let Some(api_key) = settings::get::(cx) + .openai_api_key + .clone() + { let stream = stream_completion(api_key, cx.background().clone(), request); let response = self.push_message(Role::Assistant, cx); self.push_message(Role::User, cx); diff --git a/crates/ai/src/assistant_settings.rs b/crates/ai/src/assistant_settings.rs new file mode 100644 index 0000000000..c2652ec0cb --- /dev/null +++ b/crates/ai/src/assistant_settings.rs @@ -0,0 +1,42 @@ +use anyhow; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum AssistantDockPosition { + Left, + Right, + Bottom, +} + +#[derive(Deserialize, Debug)] +pub struct AssistantSettings { + pub dock: AssistantDockPosition, + pub default_width: f32, + pub default_height: f32, + pub openai_api_key: Option, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct AssistantSettingsContent { + pub dock: Option, + pub default_width: Option, + pub default_height: Option, + pub openai_api_key: Option, +} + +impl Setting for AssistantSettings { + const KEY: Option<&'static str> = Some("assistant"); + + type FileContent = AssistantSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index 6fa920d739..85de173604 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -37,8 +37,6 @@ lazy_static.workspace = true serde.workspace = true serde_derive.workspace = true - - [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } From a81d164ea63b659a5f0cd697e9f23c5b0d387e07 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 11:38:02 +0200 Subject: [PATCH 057/131] Allow saving the OpenAI API key in the assistant panel --- Cargo.lock | 1 + assets/settings/default.json | 2 +- crates/ai/Cargo.toml | 1 + crates/ai/src/assistant.rs | 65 +++++++++++++++++++++++++++++-- crates/theme/src/theme.rs | 2 + styles/src/styleTree/assistant.ts | 22 ++++++++++- 6 files changed, 88 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e3ded5bb6..e8b2dfadd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "gpui", "isahc", "language", + "menu", "schemars", "search", "serde", diff --git a/assets/settings/default.json b/assets/settings/default.json index 695061a0aa..91868fb1e5 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -85,7 +85,7 @@ // 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": 480, + "default_width": 450, // Default height when the assistant is docked to the bottom. "default_height": 320, // OpenAI API key. diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index ce2a3338eb..9052b1e5ed 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -15,6 +15,7 @@ editor = { path = "../editor" } fs = { path = "../fs" } gpui = { path = "../gpui" } language = { path = "../language" } +menu = { path = "../menu" } search = { path = "../search" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index ecc27538ed..1bee432b7b 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -40,6 +40,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(AssistantEditor::assist); cx.capture_action(AssistantEditor::cancel_last_assist); cx.add_action(AssistantEditor::quote_selection); + cx.add_action(AssistantPanel::save_api_key); } pub enum AssistantPanelEvent { @@ -54,6 +55,7 @@ pub struct AssistantPanel { width: Option, height: Option, pane: ViewHandle, + api_key_editor: ViewHandle, languages: Arc, fs: Arc, _subscriptions: Vec, @@ -124,6 +126,17 @@ impl AssistantPanel { }); let mut this = Self { pane, + api_key_editor: cx.add_view(|cx| { + let mut editor = Editor::single_line( + Some(Arc::new(|theme| theme.assistant.api_key_editor.clone())), + cx, + ); + editor.set_placeholder_text( + "sk-000000000000000000000000000000000000000000000000", + cx, + ); + editor + }), languages: workspace.app_state().languages.clone(), fs: workspace.app_state().fs.clone(), width: None, @@ -150,6 +163,9 @@ impl AssistantPanel { .clone(); if old_openai_api_key != new_openai_api_key { old_openai_api_key = new_openai_api_key; + if this.has_focus(cx) { + cx.focus_self(); + } cx.notify(); } }), @@ -183,6 +199,17 @@ impl AssistantPanel { pane.add_item(Box::new(editor), true, focus, None, cx) }); } + + fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + let api_key = self.api_key_editor.read(cx).text(cx); + if !api_key.is_empty() { + settings::update_settings_file::( + self.fs.clone(), + cx, + move |settings| settings.openai_api_key = Some(api_key), + ); + } + } } impl Entity for AssistantPanel { @@ -195,12 +222,44 @@ impl View for AssistantPanel { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - ChildView::new(&self.pane, cx).into_any() + let style = &theme::current(cx).assistant; + if settings::get::(cx) + .openai_api_key + .is_none() + { + Flex::column() + .with_child( + Text::new( + "Paste your OpenAI API key and press Enter to use the assistant", + style.api_key_prompt.text.clone(), + ) + .aligned(), + ) + .with_child( + ChildView::new(&self.api_key_editor, cx) + .contained() + .with_style(style.api_key_editor.container) + .aligned(), + ) + .contained() + .with_style(style.api_key_prompt.container) + .aligned() + .into_any() + } else { + ChildView::new(&self.pane, cx).into_any() + } } fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { - cx.focus(&self.pane); + if settings::get::(cx) + .openai_api_key + .is_some() + { + cx.focus(&self.pane); + } else { + cx.focus(&self.api_key_editor); + } } } } @@ -290,7 +349,7 @@ impl Panel for AssistantPanel { } fn has_focus(&self, cx: &WindowContext) -> bool { - self.pane.read(cx).has_focus() + self.pane.read(cx).has_focus() || self.api_key_editor.is_focused(cx) } fn is_focus_event(event: &Self::Event) -> bool { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index f1e752b763..8282336ba5 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -976,6 +976,8 @@ pub struct AssistantStyle { pub sent_at: ContainedText, pub user_sender: ContainedText, pub assistant_sender: ContainedText, + pub api_key_editor: FieldEditor, + pub api_key_prompt: ContainedText, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 0ff65a22ae..085e43071c 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -1,5 +1,5 @@ import { ColorScheme } from "../themes/common/colorScheme" -import { text, border } from "./components" +import { text, border, background } from "./components" import editor from "./editor" export default function assistant(colorScheme: ColorScheme) { @@ -22,6 +22,26 @@ export default function assistant(colorScheme: ColorScheme) { sent_at: { margin: { top: 2, left: 8 }, ...text(layer, "sans", "default", { size: "2xs" }), + }, + apiKeyEditor: { + background: background(layer, "on"), + cornerRadius: 6, + text: text(layer, "mono", "on"), + placeholderText: text(layer, "mono", "on", "disabled", { + size: "xs", + }), + selection: colorScheme.players[0], + border: border(layer, "on"), + padding: { + bottom: 4, + left: 8, + right: 8, + top: 4, + }, + }, + apiKeyPrompt: { + padding: 10, + ...text(layer, "sans", "default", { size: "xs" }), } } } From 3750e64d9f4e46e24670bb41704bebd2ea625e7d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 12:15:25 +0200 Subject: [PATCH 058/131] Save OpenAI API key in the keychain --- assets/settings/default.json | 4 +- crates/ai/src/assistant.rs | 157 ++++++++++++++++++---------- crates/ai/src/assistant_settings.rs | 2 - 3 files changed, 100 insertions(+), 63 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 91868fb1e5..e6f006bf60 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -87,9 +87,7 @@ // 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, - // OpenAI API key. - "openai_api_key": null + "default_height": 320 }, // Whether the screen sharing icon is shown in the os status bar. "show_call_status_icon": true, diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1bee432b7b..39e9a6ba15 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -16,7 +16,7 @@ use gpui::{ use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use settings::SettingsStore; -use std::{io, sync::Arc}; +use std::{cell::Cell, io, rc::Rc, sync::Arc}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, @@ -24,7 +24,12 @@ use workspace::{ pane, Pane, Workspace, }; -actions!(assistant, [NewContext, Assist, QuoteSelection, ToggleFocus]); +const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; + +actions!( + assistant, + [NewContext, Assist, QuoteSelection, ToggleFocus, ResetKey] +); pub fn init(cx: &mut AppContext) { settings::register::(cx); @@ -41,6 +46,7 @@ pub fn init(cx: &mut AppContext) { cx.capture_action(AssistantEditor::cancel_last_assist); cx.add_action(AssistantEditor::quote_selection); cx.add_action(AssistantPanel::save_api_key); + cx.add_action(AssistantPanel::reset_api_key); } pub enum AssistantPanelEvent { @@ -55,7 +61,9 @@ pub struct AssistantPanel { width: Option, height: Option, pane: ViewHandle, - api_key_editor: ViewHandle, + api_key: Rc>>, + api_key_editor: Option>, + has_read_credentials: bool, languages: Arc, fs: Arc, _subscriptions: Vec, @@ -124,19 +132,12 @@ impl AssistantPanel { .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); pane }); + let mut this = Self { pane, - api_key_editor: cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(Arc::new(|theme| theme.assistant.api_key_editor.clone())), - cx, - ); - editor.set_placeholder_text( - "sk-000000000000000000000000000000000000000000000000", - cx, - ); - editor - }), + api_key: Rc::new(Cell::new(None)), + api_key_editor: None, + has_read_credentials: false, languages: workspace.app_state().languages.clone(), fs: workspace.app_state().fs.clone(), width: None, @@ -145,9 +146,6 @@ impl AssistantPanel { }; let mut old_dock_position = this.position(cx); - let mut old_openai_api_key = settings::get::(cx) - .openai_api_key - .clone(); this._subscriptions = vec![ cx.observe(&this.pane, |_, _, cx| cx.notify()), cx.subscribe(&this.pane, Self::handle_pane_event), @@ -157,17 +155,6 @@ impl AssistantPanel { old_dock_position = new_dock_position; cx.emit(AssistantPanelEvent::DockPositionChanged); } - - let new_openai_api_key = settings::get::(cx) - .openai_api_key - .clone(); - if old_openai_api_key != new_openai_api_key { - old_openai_api_key = new_openai_api_key; - if this.has_focus(cx) { - cx.focus_self(); - } - cx.notify(); - } }), ]; @@ -194,22 +181,49 @@ impl AssistantPanel { fn add_context(&mut self, cx: &mut ViewContext) { let focus = self.has_focus(cx); - let editor = cx.add_view(|cx| AssistantEditor::new(self.languages.clone(), cx)); + let editor = cx + .add_view(|cx| AssistantEditor::new(self.api_key.clone(), self.languages.clone(), cx)); self.pane.update(cx, |pane, cx| { pane.add_item(Box::new(editor), true, focus, None, cx) }); } fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { - let api_key = self.api_key_editor.read(cx).text(cx); - if !api_key.is_empty() { - settings::update_settings_file::( - self.fs.clone(), - cx, - move |settings| settings.openai_api_key = Some(api_key), - ); + if let Some(api_key) = self + .api_key_editor + .as_ref() + .map(|editor| editor.read(cx).text(cx)) + { + if !api_key.is_empty() { + cx.platform() + .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + self.api_key.set(Some(api_key)); + self.api_key_editor.take(); + cx.focus_self(); + cx.notify(); + } } } + + fn reset_api_key(&mut self, _: &ResetKey, cx: &mut ViewContext) { + cx.platform().delete_credentials(OPENAI_API_URL).log_err(); + self.api_key.take(); + self.api_key_editor = Some(build_api_key_editor(cx)); + cx.focus_self(); + cx.notify(); + } +} + +fn build_api_key_editor(cx: &mut ViewContext) -> ViewHandle { + cx.add_view(|cx| { + let mut editor = Editor::single_line( + Some(Arc::new(|theme| theme.assistant.api_key_editor.clone())), + cx, + ); + editor.set_placeholder_text("sk-000000000000000000000000000000000000000000000000", cx); + editor + }) } impl Entity for AssistantPanel { @@ -223,10 +237,7 @@ impl View for AssistantPanel { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let style = &theme::current(cx).assistant; - if settings::get::(cx) - .openai_api_key - .is_none() - { + if let Some(api_key_editor) = self.api_key_editor.as_ref() { Flex::column() .with_child( Text::new( @@ -236,7 +247,7 @@ impl View for AssistantPanel { .aligned(), ) .with_child( - ChildView::new(&self.api_key_editor, cx) + ChildView::new(api_key_editor, cx) .contained() .with_style(style.api_key_editor.container) .aligned(), @@ -252,13 +263,10 @@ impl View for AssistantPanel { fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { - if settings::get::(cx) - .openai_api_key - .is_some() - { - cx.focus(&self.pane); + if let Some(api_key_editor) = self.api_key_editor.as_ref() { + cx.focus(api_key_editor); } else { - cx.focus(&self.api_key_editor); + cx.focus(&self.pane); } } } @@ -323,8 +331,30 @@ impl Panel for AssistantPanel { } fn set_active(&mut self, active: bool, cx: &mut ViewContext) { - if active && self.pane.read(cx).items_len() == 0 { - self.add_context(cx); + if active { + if self.api_key.clone().take().is_none() && !self.has_read_credentials { + self.has_read_credentials = true; + let api_key = if let Some((_, api_key)) = cx + .platform() + .read_credentials(OPENAI_API_URL) + .log_err() + .flatten() + { + String::from_utf8(api_key).log_err() + } else { + None + }; + if let Some(api_key) = api_key { + self.api_key.set(Some(api_key)); + } else if self.api_key_editor.is_none() { + self.api_key_editor = Some(build_api_key_editor(cx)); + cx.notify(); + } + } + + if self.pane.read(cx).items_len() == 0 { + self.add_context(cx); + } } } @@ -349,7 +379,11 @@ impl Panel for AssistantPanel { } fn has_focus(&self, cx: &WindowContext) -> bool { - self.pane.read(cx).has_focus() || self.api_key_editor.is_focused(cx) + self.pane.read(cx).has_focus() + || self + .api_key_editor + .as_ref() + .map_or(false, |editor| editor.is_focused(cx)) } fn is_focus_event(event: &Self::Event) -> bool { @@ -364,6 +398,7 @@ struct Assistant { completion_count: usize, pending_completions: Vec, languages: Arc, + api_key: Rc>>, } impl Entity for Assistant { @@ -371,7 +406,11 @@ impl Entity for Assistant { } impl Assistant { - fn new(language_registry: Arc, cx: &mut ModelContext) -> Self { + fn new( + api_key: Rc>>, + language_registry: Arc, + cx: &mut ModelContext, + ) -> Self { let mut this = Self { buffer: cx.add_model(|_| MultiBuffer::new(0)), messages: Default::default(), @@ -379,6 +418,7 @@ impl Assistant { completion_count: Default::default(), pending_completions: Default::default(), languages: language_registry, + api_key, }; this.push_message(Role::User, cx); this @@ -399,10 +439,7 @@ impl Assistant { stream: true, }; - if let Some(api_key) = settings::get::(cx) - .openai_api_key - .clone() - { + if let Some(api_key) = self.api_key.clone().take() { let stream = stream_completion(api_key, cx.background().clone(), request); let response = self.push_message(Role::Assistant, cx); self.push_message(Role::User, cx); @@ -496,8 +533,12 @@ struct AssistantEditor { } impl AssistantEditor { - fn new(language_registry: Arc, cx: &mut ViewContext) -> Self { - let assistant = cx.add_model(|cx| Assistant::new(language_registry, cx)); + fn new( + api_key: Rc>>, + language_registry: Arc, + cx: &mut ViewContext, + ) -> Self { + let assistant = cx.add_model(|cx| Assistant::new(api_key, language_registry, cx)); let editor = cx.add_view(|cx| { let mut editor = Editor::for_multibuffer(assistant.read(cx).buffer.clone(), None, cx); editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); @@ -685,7 +726,7 @@ async fn stream_completion( let (tx, rx) = futures::channel::mpsc::unbounded::>(); let json_data = serde_json::to_string(&request)?; - let mut response = Request::post("https://api.openai.com/v1/chat/completions") + let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions")) .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", api_key)) .body(json_data)? diff --git a/crates/ai/src/assistant_settings.rs b/crates/ai/src/assistant_settings.rs index c2652ec0cb..eb92e0f6e8 100644 --- a/crates/ai/src/assistant_settings.rs +++ b/crates/ai/src/assistant_settings.rs @@ -16,7 +16,6 @@ pub struct AssistantSettings { pub dock: AssistantDockPosition, pub default_width: f32, pub default_height: f32, - pub openai_api_key: Option, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] @@ -24,7 +23,6 @@ pub struct AssistantSettingsContent { pub dock: Option, pub default_width: Option, pub default_height: Option, - pub openai_api_key: Option, } impl Setting for AssistantSettings { From 56ecfaf2f05a3b3735715e28c36247f56a2354d1 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:07:43 +0200 Subject: [PATCH 059/131] feat: add themeConfig types --- styles/src/themeConfig.ts | 137 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 styles/src/themeConfig.ts diff --git a/styles/src/themeConfig.ts b/styles/src/themeConfig.ts new file mode 100644 index 0000000000..0b93a67853 --- /dev/null +++ b/styles/src/themeConfig.ts @@ -0,0 +1,137 @@ +import { Scale, Color } from "chroma-js" +import { Syntax } from "./themes/common/syntax" + +interface ThemeMeta { + /** The name of the theme */ + name: string + /** The theme's appearance. Either `light` or `dark`. */ + appearance: ThemeAppearance + /** The author of the theme + * + * Ideally formatted as `Full Name ` + * + * Example: `John Doe ` + */ + author: string + /** SPDX License string + * + * Example: `MIT` + */ + licenseType?: string + licenseUrl?: string + themeUrl?: string +} + +export interface ThemeConfigInputColors { + neutral: Scale + red: Scale + orange: Scale + yellow: Scale + green: Scale + cyan: Scale + blue: Scale + violet: Scale + magenta: Scale +} + +export type ThemeConfigInputColorsKeys = keyof ThemeConfigInputColors + +/** Allow any part of a syntax highlight style to be overriden by the theme + * + * Example: + * ```ts + * override: { + * syntax: { + * boolean: { + * underline: true, + * }, + * }, + * } + * ``` + */ +export type ThemeConfigInputSyntax = Partial + +interface ThemeConfigOverrides { + syntax: ThemeConfigInputSyntax +} + +type ThemeConfigProperties = ThemeMeta & { + inputColor: ThemeConfigInputColors + override: ThemeConfigOverrides +} + +// This should be the format a theme is defined as +export type ThemeConfig = { + [K in keyof ThemeConfigProperties]: ThemeConfigProperties[K] +} + +interface ThemeColors { + neutral: string[] + red: string[] + orange: string[] + yellow: string[] + green: string[] + cyan: string[] + blue: string[] + violet: string[] + magenta: string[] +} + +type ThemeSyntax = Required + +export type ThemeProperties = ThemeMeta & { + color: ThemeColors + syntax: ThemeSyntax +} + +// This should be a theme after all its properties have been resolved +export type Theme = { + [K in keyof ThemeProperties]: ThemeProperties[K] +} + +export enum ThemeAppearance { + Light = "light", + Dark = "dark", +} + +export type ThemeFamilyItem = + | ThemeConfig + | { light: ThemeConfig; dark: ThemeConfig } + +type ThemeFamilyProperties = Partial> & { + name: string + default: ThemeFamilyItem + variants: { + [key: string]: ThemeFamilyItem + } +} + +// Idea: A theme family is a collection of themes that share the same name +// For example, a theme family could be `One Dark` and have a `light` and `dark` variant +// The Ayu family could have `light`, `mirage`, and `dark` variants + +type ThemeFamily = { + [K in keyof ThemeFamilyProperties]: ThemeFamilyProperties[K] +} + +/** The collection of all themes + * + * Example: + * ```ts + * { + * one_dark, + * one_light, + * ayu: { + * name: 'Ayu', + * default: 'ayu_mirage', + * variants: { + * light: 'ayu_light', + * mirage: 'ayu_mirage', + * dark: 'ayu_dark', + * }, + * }, + * ... + * } + * ``` + */ +export type ThemeIndex = Record From 6b00db75ade021880d6325db78bb957bbee15ce3 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:08:35 +0200 Subject: [PATCH 060/131] feat: update and refactor colorScheme --- styles/src/themes/common/colorScheme.ts | 188 +++++++++++++++++++- styles/src/themes/common/ramps.ts | 218 +++--------------------- 2 files changed, 212 insertions(+), 194 deletions(-) diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 5cf125ae8f..c32411f23d 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -1,6 +1,12 @@ -import { Scale } from "chroma-js" +import { Scale, Color } from "chroma-js" import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax" export { Syntax, ThemeSyntax, SyntaxHighlightStyle } +import { + ThemeConfig, + ThemeAppearance, + ThemeConfigInputColors, +} from "../../themeConfig" +import { getRamps } from "./ramps" export interface ColorScheme { name: string @@ -103,3 +109,183 @@ export interface Style { border: string foreground: string } + +export function createColorScheme(theme: ThemeConfig): ColorScheme { + const { + name, + appearance, + inputColor, + override: { syntax }, + } = theme + + const isLight = appearance === ThemeAppearance.Light + const colorRamps: ThemeConfigInputColors = inputColor + + // Chromajs scales from 0 to 1 flipped if isLight is true + const ramps = getRamps(isLight, colorRamps) + const lowest = lowestLayer(ramps) + const middle = middleLayer(ramps) + const highest = highestLayer(ramps) + + const popoverShadow = { + blur: 4, + color: ramps + .neutral(isLight ? 7 : 0) + .darken() + .alpha(0.2) + .hex(), // TODO used blend previously. Replace with something else + offset: [1, 2], + } + + const modalShadow = { + blur: 16, + color: ramps + .neutral(isLight ? 7 : 0) + .darken() + .alpha(0.2) + .hex(), // TODO used blend previously. Replace with something else + offset: [0, 2], + } + + const players = { + "0": player(ramps.blue), + "1": player(ramps.green), + "2": player(ramps.magenta), + "3": player(ramps.orange), + "4": player(ramps.violet), + "5": player(ramps.cyan), + "6": player(ramps.red), + "7": player(ramps.yellow), + } + + return { + name, + isLight, + + ramps, + + lowest, + middle, + highest, + + popoverShadow, + modalShadow, + + players, + syntax, + } +} + +function player(ramp: Scale): Player { + return { + selection: ramp(0.5).alpha(0.24).hex(), + cursor: ramp(0.5).hex(), + } +} + +function lowestLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0.2, 1), + variant: buildStyleSet(ramps.neutral, 0.2, 0.7), + on: buildStyleSet(ramps.neutral, 0.1, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function middleLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0.1, 1), + variant: buildStyleSet(ramps.neutral, 0.1, 0.7), + on: buildStyleSet(ramps.neutral, 0, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function highestLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0, 1), + variant: buildStyleSet(ramps.neutral, 0, 0.7), + on: buildStyleSet(ramps.neutral, 0.1, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function buildStyleSet( + ramp: Scale, + backgroundBase: number, + foregroundBase: number, + step: number = 0.08 +): StyleSet { + let styleDefinitions = buildStyleDefinition( + backgroundBase, + foregroundBase, + step + ) + + function colorString(indexOrColor: number | Color): string { + if (typeof indexOrColor === "number") { + return ramp(indexOrColor).hex() + } else { + return indexOrColor.hex() + } + } + + function buildStyle(style: Styles): Style { + return { + background: colorString(styleDefinitions.background[style]), + border: colorString(styleDefinitions.border[style]), + foreground: colorString(styleDefinitions.foreground[style]), + } + } + + return { + default: buildStyle("default"), + hovered: buildStyle("hovered"), + pressed: buildStyle("pressed"), + active: buildStyle("active"), + disabled: buildStyle("disabled"), + inverted: buildStyle("inverted"), + } +} + +function buildStyleDefinition( + bgBase: number, + fgBase: number, + step: number = 0.08 +) { + return { + background: { + default: bgBase, + hovered: bgBase + step, + pressed: bgBase + step * 1.5, + active: bgBase + step * 2.2, + disabled: bgBase, + inverted: fgBase + step * 6, + }, + border: { + default: bgBase + step * 1, + hovered: bgBase + step, + pressed: bgBase + step, + active: bgBase + step * 3, + disabled: bgBase + step * 0.5, + inverted: bgBase - step * 3, + }, + foreground: { + default: fgBase, + hovered: fgBase, + pressed: fgBase, + active: fgBase + step * 6, + disabled: bgBase + step * 4, + inverted: bgBase + step * 2, + }, + } +} diff --git a/styles/src/themes/common/ramps.ts b/styles/src/themes/common/ramps.ts index 746b102a84..ef93ee163f 100644 --- a/styles/src/themes/common/ramps.ts +++ b/styles/src/themes/common/ramps.ts @@ -1,14 +1,9 @@ import chroma, { Color, Scale } from "chroma-js" +import { RampSet } from "./colorScheme" import { - ColorScheme, - Layer, - Player, - RampSet, - Style, - Styles, - StyleSet, - ThemeSyntax, -} from "./colorScheme" + ThemeConfigInputColors, + ThemeConfigInputColorsKeys, +} from "../../themeConfig" export function colorRamp(color: Color): Scale { let endColor = color.desaturate(1).brighten(5) @@ -16,200 +11,37 @@ export function colorRamp(color: Color): Scale { return chroma.scale([startColor, color, endColor]).mode("lab") } -export function createColorScheme( - name: string, +/** + * Chromajs mutates the underlying ramp when you call domain. This causes problems because + we now store the ramps object in the theme so that we can pull colors out of them. + So instead of calling domain and storing the result, we have to construct new ramps for each + theme so that we don't modify the passed in ramps. + This combined with an error in the type definitions for chroma js means we have to cast the colors + function to any in order to get the colors back out from the original ramps. + * @param isLight + * @param colorRamps + * @returns + */ +export function getRamps( isLight: boolean, - colorRamps: { [rampName: string]: Scale }, - syntax?: ThemeSyntax -): ColorScheme { - // Chromajs scales from 0 to 1 flipped if isLight is true - let ramps: RampSet = {} as any + colorRamps: ThemeConfigInputColors +): RampSet { + const ramps: RampSet = {} as any + const colorsKeys = Object.keys(colorRamps) as ThemeConfigInputColorsKeys[] - // Chromajs mutates the underlying ramp when you call domain. This causes problems because - // we now store the ramps object in the theme so that we can pull colors out of them. - // So instead of calling domain and storing the result, we have to construct new ramps for each - // theme so that we don't modify the passed in ramps. - // This combined with an error in the type definitions for chroma js means we have to cast the colors - // function to any in order to get the colors back out from the original ramps. if (isLight) { - for (var rampName in colorRamps) { - ;(ramps as any)[rampName] = chroma.scale( + for (const rampName of colorsKeys) { + ramps[rampName] = chroma.scale( colorRamps[rampName].colors(100).reverse() ) } ramps.neutral = chroma.scale(colorRamps.neutral.colors(100).reverse()) } else { - for (var rampName in colorRamps) { - ;(ramps as any)[rampName] = chroma.scale( - colorRamps[rampName].colors(100) - ) + for (const rampName of colorsKeys) { + ramps[rampName] = chroma.scale(colorRamps[rampName].colors(100)) } ramps.neutral = chroma.scale(colorRamps.neutral.colors(100)) } - let lowest = lowestLayer(ramps) - let middle = middleLayer(ramps) - let highest = highestLayer(ramps) - - let popoverShadow = { - blur: 4, - color: ramps - .neutral(isLight ? 7 : 0) - .darken() - .alpha(0.2) - .hex(), // TODO used blend previously. Replace with something else - offset: [1, 2], - } - - let modalShadow = { - blur: 16, - color: ramps - .neutral(isLight ? 7 : 0) - .darken() - .alpha(0.2) - .hex(), // TODO used blend previously. Replace with something else - offset: [0, 2], - } - - let players = { - "0": player(ramps.blue), - "1": player(ramps.green), - "2": player(ramps.magenta), - "3": player(ramps.orange), - "4": player(ramps.violet), - "5": player(ramps.cyan), - "6": player(ramps.red), - "7": player(ramps.yellow), - } - - return { - name, - isLight, - - ramps, - - lowest, - middle, - highest, - - popoverShadow, - modalShadow, - - players, - syntax, - } -} - -function player(ramp: Scale): Player { - return { - selection: ramp(0.5).alpha(0.24).hex(), - cursor: ramp(0.5).hex(), - } -} - -function lowestLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0.2, 1), - variant: buildStyleSet(ramps.neutral, 0.2, 0.7), - on: buildStyleSet(ramps.neutral, 0.1, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function middleLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0.1, 1), - variant: buildStyleSet(ramps.neutral, 0.1, 0.7), - on: buildStyleSet(ramps.neutral, 0, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function highestLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0, 1), - variant: buildStyleSet(ramps.neutral, 0, 0.7), - on: buildStyleSet(ramps.neutral, 0.1, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function buildStyleSet( - ramp: Scale, - backgroundBase: number, - foregroundBase: number, - step: number = 0.08 -): StyleSet { - let styleDefinitions = buildStyleDefinition( - backgroundBase, - foregroundBase, - step - ) - - function colorString(indexOrColor: number | Color): string { - if (typeof indexOrColor === "number") { - return ramp(indexOrColor).hex() - } else { - return indexOrColor.hex() - } - } - - function buildStyle(style: Styles): Style { - return { - background: colorString(styleDefinitions.background[style]), - border: colorString(styleDefinitions.border[style]), - foreground: colorString(styleDefinitions.foreground[style]), - } - } - - return { - default: buildStyle("default"), - hovered: buildStyle("hovered"), - pressed: buildStyle("pressed"), - active: buildStyle("active"), - disabled: buildStyle("disabled"), - inverted: buildStyle("inverted"), - } -} - -function buildStyleDefinition( - bgBase: number, - fgBase: number, - step: number = 0.08 -) { - return { - background: { - default: bgBase, - hovered: bgBase + step, - pressed: bgBase + step * 1.5, - active: bgBase + step * 2.2, - disabled: bgBase, - inverted: fgBase + step * 6, - }, - border: { - default: bgBase + step * 1, - hovered: bgBase + step, - pressed: bgBase + step, - active: bgBase + step * 3, - disabled: bgBase + step * 0.5, - inverted: bgBase - step * 3, - }, - foreground: { - default: fgBase, - hovered: fgBase, - pressed: fgBase, - active: fgBase + step * 6, - disabled: bgBase + step * 4, - inverted: bgBase + step * 2, - }, - } + return ramps } From d3ed9583081cbf71635b6bcacd1b2e8558ba648d Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:10:02 +0200 Subject: [PATCH 061/131] chore: make SyntaxHighlightStyle.color optional --- styles/src/themes/common/syntax.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/themes/common/syntax.ts b/styles/src/themes/common/syntax.ts index 925ed7e5c1..1645aaea87 100644 --- a/styles/src/themes/common/syntax.ts +++ b/styles/src/themes/common/syntax.ts @@ -4,7 +4,7 @@ import { ColorScheme } from "./colorScheme" import chroma from "chroma-js" export interface SyntaxHighlightStyle { - color: string + color?: string weight?: FontWeight underline?: boolean italic?: boolean From b38f760fcdf8cec3e98ea5e91e5bbd0287b85805 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:10:19 +0200 Subject: [PATCH 062/131] feat: add index export --- styles/src/themes/common/index.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 styles/src/themes/common/index.ts diff --git a/styles/src/themes/common/index.ts b/styles/src/themes/common/index.ts new file mode 100644 index 0000000000..cd7cf9b0f4 --- /dev/null +++ b/styles/src/themes/common/index.ts @@ -0,0 +1,4 @@ +export * from "./colorScheme" +export * from "./ramps" +export * from "./syntax" +export * from "../../themeConfig" From 5fbbc1936f1743e1001038ccf46da05516051654 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:10:44 +0200 Subject: [PATCH 063/131] fix: typescript errors --- styles/src/styleTree/contactFinder.ts | 2 +- styles/src/styleTree/picker.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/styles/src/styleTree/contactFinder.ts b/styles/src/styleTree/contactFinder.ts index 42eb3b9949..7b76cde444 100644 --- a/styles/src/styleTree/contactFinder.ts +++ b/styles/src/styleTree/contactFinder.ts @@ -2,7 +2,7 @@ import picker from "./picker" import { ColorScheme } from "../themes/common/colorScheme" import { background, border, foreground, text } from "./components" -export default function contactFinder(colorScheme: ColorScheme) { +export default function contactFinder(colorScheme: ColorScheme): any { let layer = colorScheme.middle const sideMargin = 6 diff --git a/styles/src/styleTree/picker.ts b/styles/src/styleTree/picker.ts index 3245112345..66a0d2c126 100644 --- a/styles/src/styleTree/picker.ts +++ b/styles/src/styleTree/picker.ts @@ -2,7 +2,7 @@ import { ColorScheme } from "../themes/common/colorScheme" import { withOpacity } from "../utils/color" import { background, border, text } from "./components" -export default function picker(colorScheme: ColorScheme) { +export default function picker(colorScheme: ColorScheme): any { let layer = colorScheme.lowest const container = { background: background(layer), @@ -28,7 +28,7 @@ export default function picker(colorScheme: ColorScheme) { bottom: 4, }, } - const emptyInputEditor = { ...inputEditor } + const emptyInputEditor: any = { ...inputEditor } delete emptyInputEditor.border delete emptyInputEditor.margin From f97999d97f8495cccd03b7c4caf65b2fc256ebba Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Fri, 2 Jun 2023 14:11:11 +0200 Subject: [PATCH 064/131] feat: update themes to use ThemeConfig --- styles/src/themes/andromeda/andromeda.ts | 13 ++++--- .../src/themes/atelier/atelier-cave-dark.ts | 16 ++++----- .../src/themes/atelier/atelier-cave-light.ts | 16 ++++----- .../src/themes/atelier/atelier-dune-dark.ts | 16 ++++----- .../src/themes/atelier/atelier-dune-light.ts | 16 ++++----- .../themes/atelier/atelier-estuary-dark.ts | 16 ++++----- .../themes/atelier/atelier-estuary-light.ts | 14 ++++---- .../src/themes/atelier/atelier-forest-dark.ts | 16 ++++----- .../themes/atelier/atelier-forest-light.ts | 16 ++++----- .../src/themes/atelier/atelier-heath-dark.ts | 16 ++++----- .../src/themes/atelier/atelier-heath-light.ts | 16 ++++----- .../themes/atelier/atelier-lakeside-dark.ts | 16 ++++----- .../themes/atelier/atelier-lakeside-light.ts | 16 ++++----- .../themes/atelier/atelier-plateau-dark.ts | 16 ++++----- .../themes/atelier/atelier-plateau-light.ts | 14 ++++---- .../themes/atelier/atelier-savanna-dark.ts | 16 ++++----- .../themes/atelier/atelier-savanna-light.ts | 16 ++++----- .../themes/atelier/atelier-seaside-dark.ts | 16 ++++----- .../themes/atelier/atelier-seaside-light.ts | 16 ++++----- .../atelier/atelier-sulphurpool-dark.ts | 16 ++++----- .../atelier/atelier-sulphurpool-light.ts | 16 ++++----- styles/src/themes/atelier/common.ts | 2 +- styles/src/themes/ayu/ayu-dark.ts | 17 ++++----- styles/src/themes/ayu/ayu-light.ts | 17 ++++----- styles/src/themes/ayu/ayu-mirage.ts | 17 ++++----- styles/src/themes/gruvbox/gruvbox-common.ts | 35 ++++++++++++------- styles/src/themes/one/one-dark.ts | 19 +++++++--- styles/src/themes/one/one-light.ts | 19 +++++++--- styles/src/themes/rose-pine/rose-pine-dawn.ts | 13 ++++--- styles/src/themes/rose-pine/rose-pine-moon.ts | 13 ++++--- styles/src/themes/rose-pine/rose-pine.ts | 13 ++++--- styles/src/themes/sandcastle/sandcastle.ts | 13 ++++--- styles/src/themes/solarized/solarized.ts | 24 +++++++++---- styles/src/themes/summercamp/summercamp.ts | 12 +++++-- 34 files changed, 310 insertions(+), 233 deletions(-) diff --git a/styles/src/themes/andromeda/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts index d7f7f53b90..c8a075e7cb 100644 --- a/styles/src/themes/andromeda/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Andromeda" @@ -27,8 +26,6 @@ const ramps = { magenta: colorRamp(chroma("#C74DED")), } -export const dark = createColorScheme(name, false, ramps) - export const meta: Meta = { name, author: "EliverLara", @@ -37,3 +34,11 @@ export const meta: Meta = { }, url: "https://github.com/EliverLara/Andromeda", } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/atelier/atelier-cave-dark.ts b/styles/src/themes/atelier/atelier-cave-dark.ts index 41ee6a745c..d6337de5bd 100644 --- a/styles/src/themes/atelier/atelier-cave-dark.ts +++ b/styles/src/themes/atelier/atelier-cave-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-cave-light.ts b/styles/src/themes/atelier/atelier-cave-light.ts index 9e0473bbfa..8769cd0359 100644 --- a/styles/src/themes/atelier/atelier-cave-light.ts +++ b/styles/src/themes/atelier/atelier-cave-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-dune-dark.ts b/styles/src/themes/atelier/atelier-dune-dark.ts index c21ee61a60..534c174bf1 100644 --- a/styles/src/themes/atelier/atelier-dune-dark.ts +++ b/styles/src/themes/atelier/atelier-dune-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-dune-light.ts b/styles/src/themes/atelier/atelier-dune-light.ts index 956ae72c60..52bef3acfb 100644 --- a/styles/src/themes/atelier/atelier-dune-light.ts +++ b/styles/src/themes/atelier/atelier-dune-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-estuary-dark.ts b/styles/src/themes/atelier/atelier-estuary-dark.ts index b9f1880d68..550327df27 100644 --- a/styles/src/themes/atelier/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier/atelier-estuary-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-estuary-light.ts b/styles/src/themes/atelier/atelier-estuary-light.ts index a9eeb612ed..0a2b7ccda1 100644 --- a/styles/src/themes/atelier/atelier-estuary-light.ts +++ b/styles/src/themes/atelier/atelier-estuary-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor:{ neutral: chroma.scale( [ colors.base00, @@ -59,7 +59,7 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax + override: { syntax },} ) } diff --git a/styles/src/themes/atelier/atelier-forest-dark.ts b/styles/src/themes/atelier/atelier-forest-dark.ts index 352f1ea43e..5040e05603 100644 --- a/styles/src/themes/atelier/atelier-forest-dark.ts +++ b/styles/src/themes/atelier/atelier-forest-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-forest-light.ts b/styles/src/themes/atelier/atelier-forest-light.ts index 1378c9b061..cc222e501f 100644 --- a/styles/src/themes/atelier/atelier-forest-light.ts +++ b/styles/src/themes/atelier/atelier-forest-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-heath-dark.ts b/styles/src/themes/atelier/atelier-heath-dark.ts index 4c35194422..4caa5fb341 100644 --- a/styles/src/themes/atelier/atelier-heath-dark.ts +++ b/styles/src/themes/atelier/atelier-heath-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-heath-light.ts b/styles/src/themes/atelier/atelier-heath-light.ts index 51b9ef9398..dc6231957a 100644 --- a/styles/src/themes/atelier/atelier-heath-light.ts +++ b/styles/src/themes/atelier/atelier-heath-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-lakeside-dark.ts b/styles/src/themes/atelier/atelier-lakeside-dark.ts index ece9179860..5a1cdfc3a1 100644 --- a/styles/src/themes/atelier/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier/atelier-lakeside-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-lakeside-light.ts b/styles/src/themes/atelier/atelier-lakeside-light.ts index fd265d10c8..764891fb1a 100644 --- a/styles/src/themes/atelier/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier/atelier-lakeside-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-plateau-dark.ts b/styles/src/themes/atelier/atelier-plateau-dark.ts index ef64782bda..cc85940085 100644 --- a/styles/src/themes/atelier/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier/atelier-plateau-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-plateau-light.ts b/styles/src/themes/atelier/atelier-plateau-light.ts index 9fe51f5b79..3c2b920ec3 100644 --- a/styles/src/themes/atelier/atelier-plateau-light.ts +++ b/styles/src/themes/atelier/atelier-plateau-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor:{ neutral: chroma.scale( [ colors.base00, @@ -59,7 +59,7 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax + override: { syntax },} ) } diff --git a/styles/src/themes/atelier/atelier-savanna-dark.ts b/styles/src/themes/atelier/atelier-savanna-dark.ts index 36de9b817f..eecfbeb7ca 100644 --- a/styles/src/themes/atelier/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier/atelier-savanna-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-savanna-light.ts b/styles/src/themes/atelier/atelier-savanna-light.ts index d5d9cb369d..62966116ff 100644 --- a/styles/src/themes/atelier/atelier-savanna-light.ts +++ b/styles/src/themes/atelier/atelier-savanna-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-seaside-dark.ts b/styles/src/themes/atelier/atelier-seaside-dark.ts index f7c49ad71e..44ce425326 100644 --- a/styles/src/themes/atelier/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier/atelier-seaside-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-seaside-light.ts b/styles/src/themes/atelier/atelier-seaside-light.ts index 1cf6461446..0f0d830d22 100644 --- a/styles/src/themes/atelier/atelier-seaside-light.ts +++ b/styles/src/themes/atelier/atelier-seaside-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts index b4a4e2a651..2ee6c1d944 100644 --- a/styles/src/themes/atelier/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - false, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: { neutral: chroma.scale([ colors.base00, colors.base01, @@ -57,8 +57,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/atelier-sulphurpool-light.ts b/styles/src/themes/atelier/atelier-sulphurpool-light.ts index 046adbdf43..a1af81e5d5 100644 --- a/styles/src/themes/atelier/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-light.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { @@ -34,10 +33,11 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme( - meta.name, - true, - { + return createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,8 +59,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - syntax - ) + override: { syntax }, + }) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier/common.ts b/styles/src/themes/atelier/common.ts index 746834c88b..065fc506c0 100644 --- a/styles/src/themes/atelier/common.ts +++ b/styles/src/themes/atelier/common.ts @@ -1,4 +1,4 @@ -import { License, Meta, ThemeSyntax } from "../common/colorScheme" +import { License, Meta, ThemeSyntax } from "../common" export interface Variant { meta: Meta diff --git a/styles/src/themes/ayu/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts index 1dc663f161..c5d69bbae5 100644 --- a/styles/src/themes/ayu/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,17 +1,18 @@ -import { createColorScheme } from "../common/ramps" +import { createColorScheme, ThemeAppearance } from "../common" import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, - name: `${themeMeta.name} Dark` + name: `${themeMeta.name} Dark`, } const variant = ayu.dark const theme = buildTheme(variant, false) -export const dark = createColorScheme( - meta.name, - false, - theme.ramps, - theme.syntax -) +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: theme.ramps, + override: { syntax: theme.syntax }, +}) diff --git a/styles/src/themes/ayu/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts index 2543521944..50bc71b74e 100644 --- a/styles/src/themes/ayu/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,17 +1,18 @@ -import { createColorScheme } from "../common/ramps" +import { createColorScheme, ThemeAppearance } from "../common" import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, - name: `${themeMeta.name} Light` + name: `${themeMeta.name} Light`, } const variant = ayu.light const theme = buildTheme(variant, true) -export const light = createColorScheme( - meta.name, - true, - theme.ramps, - theme.syntax -) +export const light = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: theme.ramps, + override: { syntax: theme.syntax }, +}) diff --git a/styles/src/themes/ayu/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts index 2ada3678ee..71d02d2a75 100644 --- a/styles/src/themes/ayu/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,17 +1,18 @@ -import { createColorScheme } from "../common/ramps" +import { createColorScheme, ThemeAppearance } from "../common" import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, - name: `${themeMeta.name} Mirage` + name: `${themeMeta.name} Mirage`, } const variant = ayu.mirage const theme = buildTheme(variant, false) -export const dark = createColorScheme( - meta.name, - false, - theme.ramps, - theme.syntax -) +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: theme.ramps, + override: { syntax: theme.syntax }, +}) diff --git a/styles/src/themes/gruvbox/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts index b113ce68c6..bad189600c 100644 --- a/styles/src/themes/gruvbox/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -1,8 +1,20 @@ import chroma from "chroma-js" -import { Meta, ThemeSyntax } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { + Meta, + colorRamp, + createColorScheme, + ThemeSyntax, + ThemeAppearance, +} from "../common" -const name = "Gruvbox" +export const meta: Meta = { + name: "Gruvbox", + license: { + SPDX: "MIT", // "MIT/X11" + }, + author: "morhetz ", + url: "https://github.com/morhetz/gruvbox", +} const color = { dark0_hard: "#1d2021", @@ -233,7 +245,13 @@ const buildVariant = (variant: Variant) => { title: { color: colors.green }, } - return createColorScheme(name, isLight, ramps, syntax) + return createColorScheme({ + name, + author: meta.author, + appearance: variant.appearance as ThemeAppearance, + inputColor: ramps, + override: { syntax }, + }) } // Variants @@ -243,12 +261,3 @@ export const darkSoft = buildVariant(variant[2]) export const lightHard = buildVariant(variant[3]) export const lightDefault = buildVariant(variant[4]) export const lightSoft = buildVariant(variant[5]) - -export const meta: Meta = { - name, - license: { - SPDX: "MIT", // "MIT/X11" - }, - author: "morhetz ", - url: "https://github.com/morhetz/gruvbox", -} diff --git a/styles/src/themes/one/one-dark.ts b/styles/src/themes/one/one-dark.ts index 1a88ddf7dc..fe0d1a053f 100644 --- a/styles/src/themes/one/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -1,7 +1,12 @@ import chroma from "chroma-js" import { fontWeights } from "../../common" -import { Meta, ThemeSyntax } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { + Meta, + colorRamp, + createColorScheme, + ThemeSyntax, + ThemeAppearance, +} from "../common" const name = "One Dark" @@ -67,8 +72,6 @@ const syntax: ThemeSyntax = { constructor: { color: color.blue }, } -export const dark = createColorScheme(name, false, ramps, syntax) - export const meta: Meta = { name, author: "simurai", @@ -77,3 +80,11 @@ export const meta: Meta = { }, url: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax }, +}) diff --git a/styles/src/themes/one/one-light.ts b/styles/src/themes/one/one-light.ts index 231ee3abb3..ac9876ecb4 100644 --- a/styles/src/themes/one/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -1,7 +1,12 @@ import chroma from "chroma-js" import { fontWeights } from "../../common" -import { Meta, ThemeSyntax } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { + Meta, + colorRamp, + createColorScheme, + ThemeSyntax, + ThemeAppearance, +} from "../common" const name = "One Light" @@ -66,8 +71,6 @@ const syntax: ThemeSyntax = { variant: { color: color.blue }, } -export const light = createColorScheme(name, true, ramps, syntax) - export const meta: Meta = { name, author: "simurai", @@ -76,3 +79,11 @@ export const meta: Meta = { }, url: "https://github.com/atom/atom/tree/master/packages/one-light-ui", } + +export const light = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: ramps, + override: { syntax }, +}) diff --git a/styles/src/themes/rose-pine/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts index cf953872e1..e9296a67ee 100644 --- a/styles/src/themes/rose-pine/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Rosé Pine Dawn" @@ -27,8 +26,6 @@ const ramps = { magenta: colorRamp(chroma("#79549F")), } -export const light = createColorScheme(name, true, ramps) - export const meta: Meta = { name, author: "edunfelt", @@ -37,3 +34,11 @@ export const meta: Meta = { }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } + +export const light = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/rose-pine/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts index 85ac5fd73e..deef789960 100644 --- a/styles/src/themes/rose-pine/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Rosé Pine Moon" @@ -27,8 +26,6 @@ const ramps = { magenta: colorRamp(chroma("#AB6FE9")), } -export const dark = createColorScheme(name, false, ramps) - export const meta: Meta = { name, author: "edunfelt", @@ -37,3 +34,11 @@ export const meta: Meta = { }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/rose-pine/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts index fe2817be13..233d8225f3 100644 --- a/styles/src/themes/rose-pine/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Rosé Pine" @@ -25,8 +24,6 @@ const ramps = { magenta: colorRamp(chroma("#AB6FE9")), } -export const dark = createColorScheme(name, false, ramps) - export const meta: Meta = { name, author: "edunfelt", @@ -35,3 +32,11 @@ export const meta: Meta = { }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/sandcastle/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts index 2544b6399a..ab36d725a9 100644 --- a/styles/src/themes/sandcastle/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Sandcastle" @@ -25,8 +24,6 @@ const ramps = { magenta: colorRamp(chroma("#a87322")), } -export const dark = createColorScheme(name, false, ramps) - export const meta: Meta = { name, author: "gessig", @@ -35,3 +32,11 @@ export const meta: Meta = { }, url: "https://github.com/gessig/base16-sandcastle-scheme", } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/solarized/solarized.ts b/styles/src/themes/solarized/solarized.ts index 6c826fbee7..e4c86d2939 100644 --- a/styles/src/themes/solarized/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta as Metadata } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Solarized" @@ -27,10 +26,7 @@ const ramps = { magenta: colorRamp(chroma("#d33682")), } -export const dark = createColorScheme(`${name} Dark`, false, ramps) -export const light = createColorScheme(`${name} Light`, true, ramps) - -export const meta: Metadata = { +export const meta: Meta = { name, author: "Ethan Schoonover", license: { @@ -38,3 +34,19 @@ export const meta: Metadata = { }, url: "https://github.com/altercation/solarized", } + +export const dark = createColorScheme({ + name: `${name} Dark`, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) + +export const light = createColorScheme({ + name: `${name} Light`, + author: meta.author, + appearance: ThemeAppearance.Light, + inputColor: ramps, + override: { syntax: {} }, +}) diff --git a/styles/src/themes/summercamp/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts index 94c0072e4f..f9df22a224 100644 --- a/styles/src/themes/summercamp/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -1,6 +1,5 @@ import chroma from "chroma-js" -import { Meta } from "../common/colorScheme" -import { colorRamp, createColorScheme } from "../common/ramps" +import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" const name = "Summercamp" @@ -27,7 +26,6 @@ const ramps = { magenta: colorRamp(chroma("#F69BE7")), } -export const dark = createColorScheme(name, false, ramps) export const meta: Meta = { name, author: "zoefiri", @@ -36,3 +34,11 @@ export const meta: Meta = { SPDX: "MIT", }, } + +export const dark = createColorScheme({ + name: meta.name, + author: meta.author, + appearance: ThemeAppearance.Dark, + inputColor: ramps, + override: { syntax: {} }, +}) From f00f16fe3733640321ed5b31f8e744897cd0ecf2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Jun 2023 17:21:18 +0200 Subject: [PATCH 065/131] Show remaining tokens --- Cargo.lock | 43 ++++++++++++ crates/ai/Cargo.toml | 1 + crates/ai/src/assistant.rs | 108 ++++++++++++++++++++++++++++-- crates/editor/src/editor.rs | 2 +- crates/theme/src/theme.rs | 2 + styles/src/styleTree/assistant.ts | 14 ++++ 6 files changed, 162 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8b2dfadd4..9c4628e9fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,7 @@ dependencies = [ "serde_json", "settings", "theme", + "tiktoken-rs", "util", "workspace", ] @@ -745,6 +746,21 @@ dependencies = [ "which", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -870,6 +886,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", + "once_cell", + "regex-automata", "serde", ] @@ -2220,6 +2238,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -6969,6 +6997,21 @@ dependencies = [ "weezl", ] +[[package]] +name = "tiktoken-rs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba161c549e2c0686f35f5d920e63fad5cafba2c28ad2caceaf07e5d9fa6e8c4" +dependencies = [ + "anyhow", + "base64 0.21.0", + "bstr", + "fancy-regex", + "lazy_static", + "parking_lot 0.12.1", + "rustc-hash", +] + [[package]] name = "time" version = "0.1.45" diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 9052b1e5ed..e36df880d9 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -29,6 +29,7 @@ isahc.workspace = true schemars.workspace = true serde.workspace = true serde_json.workspace = true +tiktoken-rs = "0.4" [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 39e9a6ba15..68f722f1ee 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -16,7 +16,8 @@ use gpui::{ use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use settings::SettingsStore; -use std::{cell::Cell, io, rc::Rc, sync::Arc}; +use std::{cell::Cell, io, rc::Rc, sync::Arc, time::Duration}; +use tiktoken_rs::model::get_context_size; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, @@ -398,7 +399,12 @@ struct Assistant { completion_count: usize, pending_completions: Vec, languages: Arc, + model: String, + token_count: Option, + max_token_count: usize, + pending_token_count: Task>, api_key: Rc>>, + _subscriptions: Vec, } impl Entity for Assistant { @@ -411,19 +417,78 @@ impl Assistant { language_registry: Arc, cx: &mut ModelContext, ) -> Self { + let model = "gpt-3.5-turbo"; + let buffer = cx.add_model(|_| MultiBuffer::new(0)); let mut this = Self { - buffer: cx.add_model(|_| MultiBuffer::new(0)), messages: Default::default(), messages_by_id: Default::default(), completion_count: Default::default(), pending_completions: Default::default(), languages: language_registry, + token_count: None, + max_token_count: get_context_size(model), + pending_token_count: Task::ready(None), + model: model.into(), + _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], api_key, + buffer, }; this.push_message(Role::User, cx); + this.count_remaining_tokens(cx); this } + fn handle_buffer_event( + &mut self, + _: ModelHandle, + event: &editor::multi_buffer::Event, + cx: &mut ModelContext, + ) { + match event { + editor::multi_buffer::Event::ExcerptsAdded { .. } + | editor::multi_buffer::Event::ExcerptsRemoved { .. } + | editor::multi_buffer::Event::Edited => self.count_remaining_tokens(cx), + _ => {} + } + } + + fn count_remaining_tokens(&mut self, cx: &mut ModelContext) { + let messages = self + .messages + .iter() + .map(|message| tiktoken_rs::ChatCompletionRequestMessage { + role: match message.role { + Role::User => "user".into(), + Role::Assistant => "assistant".into(), + Role::System => "system".into(), + }, + content: message.content.read(cx).text(), + name: None, + }) + .collect::>(); + let model = self.model.clone(); + self.pending_token_count = cx.spawn(|this, mut cx| { + async move { + cx.background().timer(Duration::from_millis(200)).await; + let token_count = cx + .background() + .spawn(async move { tiktoken_rs::num_tokens_from_messages(&model, &messages) }) + .await?; + + this.update(&mut cx, |this, cx| { + this.token_count = Some(token_count); + cx.notify() + }); + anyhow::Ok(()) + } + .log_err() + }); + } + + fn remaining_tokens(&self) -> Option { + Some(self.max_token_count as isize - self.token_count? as isize) + } + fn assist(&mut self, cx: &mut ModelContext) { let messages = self .messages @@ -434,7 +499,7 @@ impl Assistant { }) .collect(); let request = OpenAIRequest { - model: "gpt-3.5-turbo".into(), + model: self.model.clone(), messages, stream: true, }; @@ -530,6 +595,7 @@ struct PendingCompletion { struct AssistantEditor { assistant: ModelHandle, editor: ViewHandle, + _subscriptions: Vec, } impl AssistantEditor { @@ -590,7 +656,11 @@ impl AssistantEditor { ); editor }); - Self { assistant, editor } + Self { + _subscriptions: vec![cx.observe(&assistant, |_, _, cx| cx.notify())], + assistant, + editor, + } } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { @@ -684,10 +754,34 @@ impl View for AssistantEditor { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let theme = &theme::current(cx).assistant; + let remaining_tokens = self + .assistant + .read(cx) + .remaining_tokens() + .map(|remaining_tokens| { + let remaining_tokens_style = if remaining_tokens <= 0 { + &theme.no_remaining_tokens + } else { + &theme.remaining_tokens + }; + Label::new( + remaining_tokens.to_string(), + remaining_tokens_style.text.clone(), + ) + .contained() + .with_style(remaining_tokens_style.container) + .aligned() + .top() + .right() + }); - ChildView::new(&self.editor, cx) - .contained() - .with_style(theme.container) + Stack::new() + .with_child( + ChildView::new(&self.editor, cx) + .contained() + .with_style(theme.container), + ) + .with_children(remaining_tokens) .into_any() } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ee06443068..453468349b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10,7 +10,7 @@ pub mod items; mod link_go_to_definition; mod mouse_context_menu; pub mod movement; -mod multi_buffer; +pub mod multi_buffer; mod persistence; pub mod scroll; pub mod selections_collection; diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 8282336ba5..97aac92afd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -976,6 +976,8 @@ pub struct AssistantStyle { pub sent_at: ContainedText, pub user_sender: ContainedText, pub assistant_sender: ContainedText, + pub remaining_tokens: ContainedText, + pub no_remaining_tokens: ContainedText, pub api_key_editor: FieldEditor, pub api_key_prompt: ContainedText, } diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 085e43071c..3d21ee8519 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -23,6 +23,20 @@ export default function assistant(colorScheme: ColorScheme) { margin: { top: 2, left: 8 }, ...text(layer, "sans", "default", { size: "2xs" }), }, + remaining_tokens: { + padding: 4, + margin: { right: 16, top: 4 }, + background: background(layer, "on"), + cornerRadius: 4, + ...text(layer, "sans", "positive", { size: "xs" }), + }, + no_remaining_tokens: { + padding: 4, + margin: { right: 16, top: 4 }, + background: background(layer, "on"), + cornerRadius: 4, + ...text(layer, "sans", "negative", { size: "xs" }), + }, apiKeyEditor: { background: background(layer, "on"), cornerRadius: 6, From 345fad3e9d522d0e1c8c91674606ca8c5e20de37 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:32:34 +0200 Subject: [PATCH 066/131] editor: add select previous command (#2556) Added a `select previous` command to complement `select next`. Release Notes: - Added "Select previous" editor command, mirroring `Select next`. Ticket number: Z-366 --- assets/keymaps/atom.json | 6 ++ assets/keymaps/default.json | 12 ++++ assets/keymaps/jetbrains.json | 6 ++ crates/editor/src/editor.rs | 110 ++++++++++++++++++++++++++++++ crates/editor/src/editor_tests.rs | 51 ++++++++++++++ crates/editor/src/multi_buffer.rs | 87 ++++++++++++++++++++++- crates/rope/src/rope.rs | 48 ++++++++++--- crates/text/src/text.rs | 6 ++ 8 files changed, 316 insertions(+), 10 deletions(-) diff --git a/assets/keymaps/atom.json b/assets/keymaps/atom.json index 634aed322a..25143914cc 100644 --- a/assets/keymaps/atom.json +++ b/assets/keymaps/atom.json @@ -16,6 +16,12 @@ "replace_newest": true } ], + "ctrl-cmd-g": [ + "editor::SelectPrevious", + { + "replace_newest": true + } + ], "ctrl-shift-down": "editor::AddSelectionBelow", "ctrl-shift-up": "editor::AddSelectionAbove", "cmd-shift-backspace": "editor::DeleteToBeginningOfLine", diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 7e1a8429bf..46a3fb5a5a 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -250,12 +250,24 @@ "replace_newest": false } ], + "ctrl-cmd-d": [ + "editor::SelectPrevious", + { + "replace_newest": false + } + ], "cmd-k cmd-d": [ "editor::SelectNext", { "replace_newest": true } ], + "cmd-k ctrl-cmd-d": [ + "editor::SelectPrevious", + { + "replace_newest": true + } + ], "cmd-k cmd-i": "editor::Hover", "cmd-/": [ "editor::ToggleComments", diff --git a/assets/keymaps/jetbrains.json b/assets/keymaps/jetbrains.json index 4825d3e8b5..b3e8f989a4 100644 --- a/assets/keymaps/jetbrains.json +++ b/assets/keymaps/jetbrains.json @@ -26,6 +26,12 @@ "replace_newest": false } ], + "ctrl-cmd-g": [ + "editor::SelectPrevious", + { + "replace_newest": false + } + ], "cmd-/": [ "editor::ToggleComments", { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 5c19bb0121..af7344a91f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -111,6 +111,12 @@ pub struct SelectNext { pub replace_newest: bool, } +#[derive(Clone, Deserialize, PartialEq, Default)] +pub struct SelectPrevious { + #[serde(default)] + pub replace_newest: bool, +} + #[derive(Clone, Deserialize, PartialEq)] pub struct SelectToBeginningOfLine { #[serde(default)] @@ -272,6 +278,7 @@ impl_actions!( editor, [ SelectNext, + SelectPrevious, SelectToBeginningOfLine, SelectToEndOfLine, ToggleCodeActions, @@ -367,6 +374,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::add_selection_above); cx.add_action(Editor::add_selection_below); cx.add_action(Editor::select_next); + cx.add_action(Editor::select_previous); cx.add_action(Editor::toggle_comments); cx.add_action(Editor::select_larger_syntax_node); cx.add_action(Editor::select_smaller_syntax_node); @@ -484,6 +492,7 @@ pub struct Editor { columnar_selection_tail: Option, add_selections_state: Option, select_next_state: Option, + select_prev_state: Option, selection_history: SelectionHistory, autoclose_regions: Vec, snippet_stack: InvalidationStack, @@ -539,6 +548,7 @@ pub struct EditorSnapshot { struct SelectionHistoryEntry { selections: Arc<[Selection]>, select_next_state: Option, + select_prev_state: Option, add_selections_state: Option, } @@ -1286,6 +1296,7 @@ impl Editor { columnar_selection_tail: None, add_selections_state: None, select_next_state: None, + select_prev_state: None, selection_history: Default::default(), autoclose_regions: Default::default(), snippet_stack: Default::default(), @@ -1507,6 +1518,7 @@ impl Editor { let buffer = &display_map.buffer_snapshot; self.add_selections_state = None; self.select_next_state = None; + self.select_prev_state = None; self.select_larger_syntax_node_stack.clear(); self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer); self.snippet_stack @@ -5213,6 +5225,101 @@ impl Editor { } } + pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext) { + self.push_to_selection_history(); + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let buffer = &display_map.buffer_snapshot; + let mut selections = self.selections.all::(cx); + if let Some(mut select_prev_state) = self.select_prev_state.take() { + let query = &select_prev_state.query; + if !select_prev_state.done { + let first_selection = selections.iter().min_by_key(|s| s.id).unwrap(); + let last_selection = selections.iter().max_by_key(|s| s.id).unwrap(); + let mut next_selected_range = None; + // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer. + let bytes_before_last_selection = + buffer.reversed_bytes_in_range(0..last_selection.start); + let bytes_after_first_selection = + buffer.reversed_bytes_in_range(first_selection.end..buffer.len()); + let query_matches = query + .stream_find_iter(bytes_before_last_selection) + .map(|result| (last_selection.start, result)) + .chain( + query + .stream_find_iter(bytes_after_first_selection) + .map(|result| (buffer.len(), result)), + ); + for (end_offset, query_match) in query_matches { + let query_match = query_match.unwrap(); // can only fail due to I/O + let offset_range = + end_offset - query_match.end()..end_offset - query_match.start(); + let display_range = offset_range.start.to_display_point(&display_map) + ..offset_range.end.to_display_point(&display_map); + + if !select_prev_state.wordwise + || (!movement::is_inside_word(&display_map, display_range.start) + && !movement::is_inside_word(&display_map, display_range.end)) + { + next_selected_range = Some(offset_range); + break; + } + } + + if let Some(next_selected_range) = next_selected_range { + self.unfold_ranges([next_selected_range.clone()], false, true, cx); + self.change_selections(Some(Autoscroll::newest()), cx, |s| { + if action.replace_newest { + s.delete(s.newest_anchor().id); + } + s.insert_range(next_selected_range); + }); + } else { + select_prev_state.done = true; + } + } + + self.select_prev_state = Some(select_prev_state); + } else if selections.len() == 1 { + let selection = selections.last_mut().unwrap(); + if selection.start == selection.end { + let word_range = movement::surrounding_word( + &display_map, + selection.start.to_display_point(&display_map), + ); + selection.start = word_range.start.to_offset(&display_map, Bias::Left); + selection.end = word_range.end.to_offset(&display_map, Bias::Left); + selection.goal = SelectionGoal::None; + selection.reversed = false; + + let query = buffer + .text_for_range(selection.start..selection.end) + .collect::(); + let query = query.chars().rev().collect::(); + let select_state = SelectNextState { + query: AhoCorasick::new_auto_configured(&[query]), + wordwise: true, + done: false, + }; + self.unfold_ranges([selection.start..selection.end], false, true, cx); + self.change_selections(Some(Autoscroll::newest()), cx, |s| { + s.select(selections); + }); + self.select_prev_state = Some(select_state); + } else { + let query = buffer + .text_for_range(selection.start..selection.end) + .collect::(); + let query = query.chars().rev().collect::(); + self.select_prev_state = Some(SelectNextState { + query: AhoCorasick::new_auto_configured(&[query]), + wordwise: false, + done: false, + }); + self.select_previous(action, cx); + } + } + } + pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let mut selections = this.selections.all::(cx); @@ -5586,6 +5693,7 @@ impl Editor { if let Some(entry) = self.selection_history.undo_stack.pop_back() { self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec())); self.select_next_state = entry.select_next_state; + self.select_prev_state = entry.select_prev_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::newest(), cx); } @@ -5598,6 +5706,7 @@ impl Editor { if let Some(entry) = self.selection_history.redo_stack.pop_back() { self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec())); self.select_next_state = entry.select_next_state; + self.select_prev_state = entry.select_prev_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::newest(), cx); } @@ -6375,6 +6484,7 @@ impl Editor { self.selection_history.push(SelectionHistoryEntry { selections: self.selections.disjoint_anchors(), select_next_state: self.select_next_state.clone(), + select_prev_state: self.select_prev_state.clone(), add_selections_state: self.add_selections_state.clone(), }); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index bc671b9ffc..969b9c882d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3107,6 +3107,57 @@ async fn test_select_next(cx: &mut gpui::TestAppContext) { cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); } +#[gpui::test] +async fn test_select_previous(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + { + // `Select previous` without a selection (selects wordwise) + let mut cx = EditorTestContext::new(cx).await; + cx.set_state("abc\nˇabc abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc"); + + cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx)); + cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); + + cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); + } + { + // `Select previous` with a selection + let mut cx = EditorTestContext::new(cx).await; + cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»"); + + cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc"); + + cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»"); + + cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»"); + } +} + #[gpui::test] async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index b7b2b89c8c..af0003ec2d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -196,6 +196,13 @@ pub struct MultiBufferBytes<'a> { chunk: &'a [u8], } +pub struct ReversedMultiBufferBytes<'a> { + range: Range, + excerpts: Cursor<'a, Excerpt, usize>, + excerpt_bytes: Option>, + chunk: &'a [u8], +} + struct ExcerptChunks<'a> { content_chunks: BufferChunks<'a>, footer_height: usize, @@ -1967,7 +1974,6 @@ impl MultiBufferSnapshot { } else { None }; - MultiBufferBytes { range, excerpts, @@ -1976,6 +1982,33 @@ impl MultiBufferSnapshot { } } + pub fn reversed_bytes_in_range( + &self, + range: Range, + ) -> ReversedMultiBufferBytes { + let range = range.start.to_offset(self)..range.end.to_offset(self); + let mut excerpts = self.excerpts.cursor::(); + excerpts.seek(&range.end, Bias::Left, &()); + + let mut chunk = &[][..]; + let excerpt_bytes = if let Some(excerpt) = excerpts.item() { + let mut excerpt_bytes = excerpt.reversed_bytes_in_range( + range.start - excerpts.start()..range.end - excerpts.start(), + ); + chunk = excerpt_bytes.next().unwrap_or(&[][..]); + Some(excerpt_bytes) + } else { + None + }; + + ReversedMultiBufferBytes { + range, + excerpts, + excerpt_bytes, + chunk, + } + } + pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows { let mut result = MultiBufferRows { buffer_row_range: 0..0, @@ -3409,6 +3442,26 @@ impl Excerpt { } } + fn reversed_bytes_in_range(&self, range: Range) -> ExcerptBytes { + let content_start = self.range.context.start.to_offset(&self.buffer); + let bytes_start = content_start + range.start; + let bytes_end = content_start + cmp::min(range.end, self.text_summary.len); + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.len + && range.end > self.text_summary.len + { + 1 + } else { + 0 + }; + let content_bytes = self.buffer.reversed_bytes_in_range(bytes_start..bytes_end); + + ExcerptBytes { + content_bytes, + footer_height, + } + } + fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor { if text_anchor .cmp(&self.range.context.start, &self.buffer) @@ -3727,6 +3780,38 @@ impl<'a> io::Read for MultiBufferBytes<'a> { } } +impl<'a> ReversedMultiBufferBytes<'a> { + fn consume(&mut self, len: usize) { + self.range.end -= len; + self.chunk = &self.chunk[..self.chunk.len() - len]; + + if !self.range.is_empty() && self.chunk.is_empty() { + if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) { + self.chunk = chunk; + } else { + self.excerpts.next(&()); + if let Some(excerpt) = self.excerpts.item() { + let mut excerpt_bytes = + excerpt.bytes_in_range(0..self.range.end - self.excerpts.start()); + self.chunk = excerpt_bytes.next().unwrap(); + self.excerpt_bytes = Some(excerpt_bytes); + } + } + } + } +} + +impl<'a> io::Read for ReversedMultiBufferBytes<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(buf.len(), self.chunk.len()); + buf[..len].copy_from_slice(&self.chunk[..len]); + buf[..len].reverse(); + if len > 0 { + self.consume(len); + } + Ok(len) + } +} impl<'a> Iterator for ExcerptBytes<'a> { type Item = &'a [u8]; diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 797fb39317..6b6f364fdb 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -179,7 +179,11 @@ impl Rope { } pub fn bytes_in_range(&self, range: Range) -> Bytes { - Bytes::new(self, range) + Bytes::new(self, range, false) + } + + pub fn reversed_bytes_in_range(&self, range: Range) -> Bytes { + Bytes::new(self, range, true) } pub fn chunks(&self) -> Chunks { @@ -579,22 +583,33 @@ impl<'a> Iterator for Chunks<'a> { pub struct Bytes<'a> { chunks: sum_tree::Cursor<'a, Chunk, usize>, range: Range, + reversed: bool, } impl<'a> Bytes<'a> { - pub fn new(rope: &'a Rope, range: Range) -> Self { + pub fn new(rope: &'a Rope, range: Range, reversed: bool) -> Self { let mut chunks = rope.chunks.cursor(); - chunks.seek(&range.start, Bias::Right, &()); - Self { chunks, range } + if reversed { + chunks.seek(&range.end, Bias::Left, &()); + } else { + chunks.seek(&range.start, Bias::Right, &()); + } + Self { + chunks, + range, + reversed, + } } pub fn peek(&self) -> Option<&'a [u8]> { let chunk = self.chunks.item()?; + if self.reversed && self.range.start >= self.chunks.end(&()) { + return None; + } let chunk_start = *self.chunks.start(); if self.range.end <= chunk_start { return None; } - let start = self.range.start.saturating_sub(chunk_start); let end = self.range.end - chunk_start; Some(&chunk.0.as_bytes()[start..chunk.0.len().min(end)]) @@ -607,7 +622,11 @@ impl<'a> Iterator for Bytes<'a> { fn next(&mut self) -> Option { let result = self.peek(); if result.is_some() { - self.chunks.next(&()); + if self.reversed { + self.chunks.prev(&()); + } else { + self.chunks.next(&()); + } } result } @@ -617,10 +636,21 @@ impl<'a> io::Read for Bytes<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { if let Some(chunk) = self.peek() { let len = cmp::min(buf.len(), chunk.len()); - buf[..len].copy_from_slice(&chunk[..len]); - self.range.start += len; + if self.reversed { + buf[..len].copy_from_slice(&chunk[chunk.len() - len..]); + buf[..len].reverse(); + self.range.end -= len; + } else { + buf[..len].copy_from_slice(&chunk[..len]); + self.range.start += len; + } + if len == chunk.len() { - self.chunks.next(&()); + if self.reversed { + self.chunks.prev(&()); + } else { + self.chunks.next(&()); + } } Ok(len) } else { diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index dcfaf818d1..2693add8ed 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1749,6 +1749,12 @@ impl BufferSnapshot { self.visible_text.bytes_in_range(start..end) } + pub fn reversed_bytes_in_range(&self, range: Range) -> rope::Bytes<'_> { + let start = range.start.to_offset(self); + let end = range.end.to_offset(self); + self.visible_text.reversed_bytes_in_range(start..end) + } + pub fn text_for_range(&self, range: Range) -> Chunks<'_> { let start = range.start.to_offset(self); let end = range.end.to_offset(self); From f6a4706410ed9d547cd3757c41ee63e76a97bce3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 2 Jun 2023 12:32:36 -0700 Subject: [PATCH 067/131] Improve panic reports * Add an 'identifying_backtrace' field that only contains symbols in our codebase, which can be used for better deduplication. * In the main backtrace, include file and line numbers for all symbols in our codebase --- crates/zed/src/main.rs | 85 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e22edee5f6..6248ccf4f0 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -32,6 +32,7 @@ use std::{ ffi::OsStr, fs::OpenOptions, io::Write as _, + ops::Not, os::unix::prelude::OsStrExt, panic, path::{Path, PathBuf}, @@ -371,13 +372,12 @@ struct Panic { #[serde(skip_serializing_if = "Option::is_none")] location_data: Option, backtrace: Vec, - // TODO - // stripped_backtrace: String, release_channel: String, os_name: String, os_version: Option, architecture: String, panicked_on: u128, + identifying_backtrace: Option>, } #[derive(Serialize)] @@ -396,18 +396,73 @@ fn init_panic_hook(app: &App) { let app_version = ZED_APP_VERSION .or_else(|| platform.app_version().ok()) .map_or("dev".to_string(), |v| v.to_string()); - let backtrace = Backtrace::new(); let thread = thread::current(); let thread = thread.name().unwrap_or(""); - let payload = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &**s, - None => "Box", - }, - }; + let payload = info.payload(); + let payload = None + .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string())) + .or_else(|| payload.downcast_ref::().map(|s| s.clone())) + .unwrap_or_else(|| "Box".to_string()); + + let backtrace = Backtrace::new(); + let backtrace = backtrace + .frames() + .iter() + .filter_map(|frame| { + let symbol = frame.symbols().first()?; + let path = symbol.filename()?; + Some((path, symbol.lineno(), format!("{:#}", symbol.name()?))) + }) + .collect::>(); + + let this_file_path = Path::new(file!()); + + // Find the first frame in the backtrace for this panic hook itself. Exclude + // that frame and all frames before it. + let mut start_frame_ix = 0; + let mut codebase_root_path = None; + for (ix, (path, _, _)) in backtrace.iter().enumerate() { + if path.ends_with(this_file_path) { + start_frame_ix = ix + 1; + codebase_root_path = path.ancestors().nth(this_file_path.components().count()); + break; + } + } + + // Exclude any subsequent frames inside of rust's panic handling system. + while let Some((path, _, _)) = backtrace.get(start_frame_ix) { + if path.starts_with("/rustc") { + start_frame_ix += 1; + } else { + break; + } + } + + // Build two backtraces: + // * one for display, which includes symbol names for all frames, and files + // and line numbers for symbols in this codebase + // * one for identification and de-duplication, which only includes symbol + // names for symbols in this codebase. + let mut display_backtrace = Vec::new(); + let mut identifying_backtrace = Vec::new(); + for (path, line, symbol) in &backtrace[start_frame_ix..] { + display_backtrace.push(symbol.clone()); + + if let Some(codebase_root_path) = &codebase_root_path { + if let Ok(suffix) = path.strip_prefix(&codebase_root_path) { + identifying_backtrace.push(symbol.clone()); + + let display_path = suffix.to_string_lossy(); + if let Some(line) = line { + display_backtrace.push(format!(" {display_path}:{line}")); + } else { + display_backtrace.push(format!(" {display_path}")); + } + } + } + } let panic_data = Panic { thread: thread.into(), @@ -416,11 +471,6 @@ fn init_panic_hook(app: &App) { file: location.file().into(), line: location.line(), }), - backtrace: format!("{:?}", backtrace) - .split("\n") - .map(|line| line.to_string()) - .collect(), - // modified_backtrace: None, release_channel: RELEASE_CHANNEL.dev_name().into(), os_name: platform.os_name().into(), os_version: platform @@ -432,6 +482,11 @@ fn init_panic_hook(app: &App) { .duration_since(UNIX_EPOCH) .unwrap() .as_millis(), + backtrace: display_backtrace, + identifying_backtrace: identifying_backtrace + .is_empty() + .not() + .then_some(identifying_backtrace), }; if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() { From 7417835f06a851ad1c6a6416727dbd06f15a1bf5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 2 Jun 2023 13:02:17 -0700 Subject: [PATCH 068/131] Avoid writing spurious nulls to settings file when updating it programatically --- crates/settings/src/settings_store.rs | 62 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 3fdb81dd45..1133cb5970 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -623,22 +623,6 @@ impl AnySettingValue for SettingValue { } } -// impl Debug for SettingsStore { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// return f -// .debug_struct("SettingsStore") -// .field( -// "setting_value_sets_by_type", -// &self -// .setting_values -// .values() -// .map(|set| (set.setting_type_name(), set)) -// .collect::>(), -// ) -// .finish_non_exhaustive(); -// } -// } - fn update_value_in_json_text<'a>( text: &mut String, key_path: &mut Vec<&'a str>, @@ -681,6 +665,10 @@ fn update_value_in_json_text<'a>( key_path.pop(); } } else if old_value != new_value { + let mut new_value = new_value.clone(); + if let Some(new_object) = new_value.as_object_mut() { + new_object.retain(|_, v| !v.is_null()); + } let (range, replacement) = replace_value_in_json_text(text, &key_path, tab_size, &new_value); text.replace_range(range.clone(), &replacement); @@ -692,7 +680,7 @@ fn replace_value_in_json_text( text: &str, key_path: &[&str], tab_size: usize, - new_value: impl Serialize, + new_value: &serde_json::Value, ) -> (Range, String) { const LANGUAGE_OVERRIDES: &'static str = "language_overrides"; const LANGUAGES: &'static str = "languages"; @@ -1039,24 +1027,32 @@ mod tests { r#"{ "languages": { "JSON": { - "is_enabled": true + "language_setting_1": true } } }"# .unindent(), |settings| { - settings.languages.get_mut("JSON").unwrap().is_enabled = false; settings .languages - .insert("Rust".into(), LanguageSettingEntry { is_enabled: true }); + .get_mut("JSON") + .unwrap() + .language_setting_1 = Some(false); + settings.languages.insert( + "Rust".into(), + LanguageSettingEntry { + language_setting_2: Some(true), + ..Default::default() + }, + ); }, r#"{ "languages": { "Rust": { - "is_enabled": true + "language_setting_2": true }, "JSON": { - "is_enabled": false + "language_setting_1": false } } }"# @@ -1119,6 +1115,23 @@ mod tests { .unindent(), cx, ); + + check_settings_update::( + &mut store, + r#"{ + } + "# + .unindent(), + |settings| settings.age = Some(37), + r#"{ + "user": { + "age": 37 + } + } + "# + .unindent(), + cx, + ); } fn check_settings_update( @@ -1247,9 +1260,10 @@ mod tests { languages: HashMap, } - #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] + #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] struct LanguageSettingEntry { - is_enabled: bool, + language_setting_1: Option, + language_setting_2: Option, } impl Setting for LanguageSettings { From 7c60f636d5d0d947137e750461219e15a588c655 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Fri, 2 Jun 2023 22:02:19 -0400 Subject: [PATCH 069/131] Fix typos --- assets/keymaps/default.json | 2 +- assets/settings/default.json | 2 +- crates/cli/src/main.rs | 5 +---- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/copilot/src/copilot.rs | 2 +- crates/editor/src/display_map.rs | 4 ++-- crates/editor/src/editor.rs | 6 +++--- crates/editor/src/editor_tests.rs | 6 +++--- crates/editor/src/element.rs | 2 +- crates/editor/src/multi_buffer.rs | 2 +- crates/editor/src/test.rs | 4 ++-- crates/fs/src/fs.rs | 6 +++--- crates/gpui/src/app.rs | 4 ++-- crates/gpui/src/keymap_matcher.rs | 2 +- crates/language/src/language_settings.rs | 2 +- crates/language/src/syntax_map.rs | 4 ++-- crates/plugin_macros/src/lib.rs | 4 ++-- crates/plugin_runtime/OPAQUE.md | 4 ++-- crates/plugin_runtime/src/plugin.rs | 10 +++++----- crates/project/src/project.rs | 2 +- crates/project/src/worktree.rs | 4 ++-- crates/project_symbols/src/project_symbols.rs | 2 +- crates/rpc/proto/zed.proto | 2 +- crates/rpc/src/auth.rs | 2 +- crates/sqlez/src/thread_safe_connection.rs | 8 ++++---- crates/terminal/src/mappings/colors.rs | 4 ++-- crates/terminal_view/README.md | 2 +- crates/terminal_view/scripts/print256color.sh | 2 +- crates/terminal_view/src/terminal_element.rs | 4 ++-- crates/terminal_view/src/terminal_view.rs | 4 ++-- crates/text/src/tests.rs | 4 ++-- crates/vim/src/normal.rs | 2 +- crates/vim/src/test/neovim_connection.rs | 2 +- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/workspace.rs | 2 +- crates/zed/src/languages/typescript.rs | 2 +- crates/zed/src/main.rs | 2 +- styles/src/buildLicenses.ts | 2 +- styles/src/styleTree/editor.ts | 2 +- styles/src/styleTree/search.ts | 2 +- styles/src/system/types.ts | 10 +++++----- 41 files changed, 69 insertions(+), 72 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 46a3fb5a5a..d37bc7d2ed 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -516,7 +516,7 @@ "terminal::SendText", "\u0001" ], - // Terminal.app compatability + // Terminal.app compatibility "alt-left": [ "terminal::SendText", "\u001bb" diff --git a/assets/settings/default.json b/assets/settings/default.json index 23599c8dfb..8a7a215e9d 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -245,7 +245,7 @@ // copy to the system clipboard. "copy_on_select": false, // Any key-value pairs added to this list will be added to the terminal's - // enviroment. Use `:` to seperate multiple values. + // environment. Use `:` to separate multiple values. "env": { // "KEY": "value1:value2" }, diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index feebbff61b..bdf677512c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -159,10 +159,7 @@ impl Bundle { fn path(&self) -> &Path { match self { Self::App { app_bundle, .. } => app_bundle, - Self::LocalPath { - executable: excutable, - .. - } => excutable, + Self::LocalPath { executable, .. } => executable, } } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index eb1755a9ff..720a73f477 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -472,7 +472,7 @@ impl CollabTitlebarItem { Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { - //TODO: Ensure this button has consistant width for both text variations + //TODO: Ensure this button has consistent width for both text variations let style = titlebar.share_button.style_for(state, false); Label::new(label, style.text.clone()) .contained() diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 7834603552..04e8764f7b 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -375,7 +375,7 @@ impl Copilot { server .on_notification::(|params, _cx| { match params.level { - // Copilot is pretty agressive about logging + // Copilot is pretty aggressive about logging 0 => debug!("copilot: {}", params.message), 1 => debug!("copilot: {}", params.message), _ => error!("copilot: {}", params.message), diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index b0483db68d..a594af51a6 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -475,7 +475,7 @@ impl DisplaySnapshot { }) } - /// Returns an iterator of the start positions of the occurances of `target` in the `self` after `from` + /// Returns an iterator of the start positions of the occurrences of `target` in the `self` after `from` /// Stops if `condition` returns false for any of the character position pairs observed. pub fn find_while<'a>( &'a self, @@ -486,7 +486,7 @@ impl DisplaySnapshot { Self::find_internal(self.chars_at(from), target.chars().collect(), condition) } - /// Returns an iterator of the end positions of the occurances of `target` in the `self` before `from` + /// Returns an iterator of the end positions of the occurrences of `target` in the `self` before `from` /// Stops if `condition` returns false for any of the character position pairs observed. pub fn reverse_find_while<'a>( &'a self, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index af7344a91f..d98bd92532 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2539,7 +2539,7 @@ impl Editor { .read(cx) .text_anchor_for_position(position.clone(), cx)?; - // OnTypeFormatting retuns a list of edits, no need to pass them between Zed instances, + // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances, // hence we do LSP request & edit on host side only — add formats to host's history. let push_to_lsp_host_history = true; // If this is not the host, append its history with new edits. @@ -7913,13 +7913,13 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend } pub fn highlight_diagnostic_message( - inital_highlights: Vec, + initial_highlights: Vec, message: &str, ) -> (String, Vec) { let mut message_without_backticks = String::new(); let mut prev_offset = 0; let mut inside_block = false; - let mut highlights = inital_highlights; + let mut highlights = initial_highlights; for (match_ix, (offset, _)) in message .match_indices('`') .chain([(message.len(), "")]) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 969b9c882d..35d61bf0cf 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -4321,7 +4321,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { ); assert!(!cx.read(|cx| editor.is_dirty(cx))); - // Set rust language override and assert overriden tabsize is sent to language server + // Set rust language override and assert overridden tabsize is sent to language server update_test_settings(cx, |settings| { settings.languages.insert( "Rust".into(), @@ -4435,7 +4435,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { ); assert!(!cx.read(|cx| editor.is_dirty(cx))); - // Set rust language override and assert overriden tabsize is sent to language server + // Set rust language override and assert overridden tabsize is sent to language server update_test_settings(cx, |settings| { settings.languages.insert( "Rust".into(), @@ -4776,7 +4776,7 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { two threeˇ "}, - "overlapping aditional edit", + "overlapping additional edit", ), ( indoc! {" diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 6be0650848..a74775e7f6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3080,7 +3080,7 @@ mod tests { editor_width: f32, ) -> Vec { info!( - "Creating editor with mode {editor_mode:?}, witdh {editor_width} and text '{input_text}'" + "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" ); let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&input_text, cx); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index af0003ec2d..28b60f4c02 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -5332,7 +5332,7 @@ mod tests { assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678"); // An undo in the multibuffer undoes the multibuffer transaction - // and also any individual buffer edits that have occured since + // and also any individual buffer edits that have occurred since // that transaction. multibuffer.undo(cx); assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678"); diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 8a3e08bea5..9b568e4a4f 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -48,8 +48,8 @@ pub fn marked_display_snapshot( } pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext) { - let (umarked_text, text_ranges) = marked_text_ranges(marked_text, true); - assert_eq!(editor.text(cx), umarked_text); + let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true); + assert_eq!(editor.text(cx), unmarked_text); editor.change_selections(None, cx, |s| s.select_ranges(text_ranges)); } diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 99562405b5..4c6b6c24d3 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -32,7 +32,7 @@ use repository::{FakeGitRepositoryState, GitFileStatus}; use std::sync::Weak; lazy_static! { - static ref LINE_SEPERATORS_REGEX: Regex = Regex::new("\r\n|\r|\u{2028}|\u{2029}").unwrap(); + static ref LINE_SEPARATORS_REGEX: Regex = Regex::new("\r\n|\r|\u{2028}|\u{2029}").unwrap(); } #[derive(Clone, Copy, Debug, PartialEq)] @@ -77,13 +77,13 @@ impl LineEnding { } pub fn normalize(text: &mut String) { - if let Cow::Owned(replaced) = LINE_SEPERATORS_REGEX.replace_all(text, "\n") { + if let Cow::Owned(replaced) = LINE_SEPARATORS_REGEX.replace_all(text, "\n") { *text = replaced; } } pub fn normalize_arc(text: Arc) -> Arc { - if let Cow::Owned(replaced) = LINE_SEPERATORS_REGEX.replace_all(&text, "\n") { + if let Cow::Owned(replaced) = LINE_SEPARATORS_REGEX.replace_all(&text, "\n") { replaced.into() } else { text diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index d0388a9aed..882800f128 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -6335,9 +6335,9 @@ mod tests { #[crate::test(self)] async fn test_labeled_tasks(cx: &mut TestAppContext) { assert_eq!(None, cx.update(|cx| cx.active_labeled_tasks().next())); - let (mut sender, mut reciever) = postage::oneshot::channel::<()>(); + let (mut sender, mut receiver) = postage::oneshot::channel::<()>(); let task = cx - .update(|cx| cx.spawn_labeled("Test Label", |_| async move { reciever.recv().await })); + .update(|cx| cx.spawn_labeled("Test Label", |_| async move { receiver.recv().await })); assert_eq!( Some("Test Label"), diff --git a/crates/gpui/src/keymap_matcher.rs b/crates/gpui/src/keymap_matcher.rs index 0c088f9728..bc70638b2c 100644 --- a/crates/gpui/src/keymap_matcher.rs +++ b/crates/gpui/src/keymap_matcher.rs @@ -67,7 +67,7 @@ impl KeymapMatcher { /// MatchResult::Pending => /// There exist bindings which are still waiting for more keys. /// MatchResult::Complete(matches) => - /// 1 or more bindings have recieved the necessary key presses. + /// 1 or more bindings have received the necessary key presses. /// The order of the matched actions is by position of the matching first, // and order in the keymap second. pub fn push_keystroke( diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 332e789b4d..1a953b0bf2 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -264,7 +264,7 @@ impl settings::Setting for AllLanguageSettings { let mut root_schema = generator.root_schema_for::(); // Create a schema for a 'languages overrides' object, associating editor - // settings with specific langauges. + // settings with specific languages. assert!(root_schema .definitions .contains_key("LanguageSettingsContent")); diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index ce1196e946..16313db498 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -773,7 +773,7 @@ impl<'a> SyntaxMapCaptures<'a> { } in layers { let grammar = match &language.grammar { - Some(grammer) => grammer, + Some(grammar) => grammar, None => continue, }; let query = match query(&grammar) { @@ -896,7 +896,7 @@ impl<'a> SyntaxMapMatches<'a> { } in layers { let grammar = match &language.grammar { - Some(grammer) => grammer, + Some(grammar) => grammar, None => continue, }; let query = match query(&grammar) { diff --git a/crates/plugin_macros/src/lib.rs b/crates/plugin_macros/src/lib.rs index 3f708658fd..2fe8b31b61 100644 --- a/crates/plugin_macros/src/lib.rs +++ b/crates/plugin_macros/src/lib.rs @@ -11,7 +11,7 @@ use syn::{parse_macro_input, Block, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Ty /// "Hello from Wasm".into() /// } /// ``` -/// This macro makes a function defined guest-side avaliable host-side. +/// This macro makes a function defined guest-side available host-side. /// Note that all arguments and return types must be `serde`. #[proc_macro_attribute] pub fn export(args: TokenStream, function: TokenStream) -> TokenStream { @@ -92,7 +92,7 @@ pub fn export(args: TokenStream, function: TokenStream) -> TokenStream { /// #[import] /// pub fn operating_system_name() -> String; /// ``` -/// This macro makes a function defined host-side avaliable guest-side. +/// This macro makes a function defined host-side available guest-side. /// Note that all arguments and return types must be `serde`. /// All that's provided is a signature, as the function is implemented host-side. #[proc_macro_attribute] diff --git a/crates/plugin_runtime/OPAQUE.md b/crates/plugin_runtime/OPAQUE.md index c99224edeb..52ee75dbf9 100644 --- a/crates/plugin_runtime/OPAQUE.md +++ b/crates/plugin_runtime/OPAQUE.md @@ -127,7 +127,7 @@ use plugin_handles::RopeHandle; pub fn append(rope: RopeHandle, string: &str); ``` -This allows us to perform an operation on a `Rope`, but how do we get a `RopeHandle` into a plugin? Well, as plugins, we can only aquire resources to handles we're given, so we'd need to expose a fuction that takes a handle. +This allows us to perform an operation on a `Rope`, but how do we get a `RopeHandle` into a plugin? Well, as plugins, we can only acquire resources to handles we're given, so we'd need to expose a function that takes a handle. To illustrate that point, here's an example. First, we'd define a plugin-side function as follows: @@ -177,7 +177,7 @@ So here's what calling `append_newline` would do, from the top: 6. And from here on out we return up the callstack, through Wasm, to Rust all the way back to where we started. Right before we return, we clear out the `ResourcePool`, so that we're no longer holding onto the underlying resource. -Throughout this entire chain of calls, the resource remain host-side. By temporarilty checking it into a `ResourcePool`, we're able to keep a reference to the resource that we can use, while avoiding copying the uncopyable resource. +Throughout this entire chain of calls, the resource remain host-side. By temporarily checking it into a `ResourcePool`, we're able to keep a reference to the resource that we can use, while avoiding copying the uncopyable resource. ## Final Notes diff --git a/crates/plugin_runtime/src/plugin.rs b/crates/plugin_runtime/src/plugin.rs index bfcb024faa..8070539b35 100644 --- a/crates/plugin_runtime/src/plugin.rs +++ b/crates/plugin_runtime/src/plugin.rs @@ -132,7 +132,7 @@ impl PluginBuilder { "env", &format!("__{}", name), move |mut caller: Caller<'_, WasiCtxAlloc>, packed_buffer: u64| { - // TODO: use try block once avaliable + // TODO: use try block once available let result: Result<(WasiBuffer, Memory, _), Trap> = (|| { // grab a handle to the memory let plugin_memory = match caller.get_export("memory") { @@ -211,7 +211,7 @@ impl PluginBuilder { "env", &format!("__{}", name), move |mut caller: Caller<'_, WasiCtxAlloc>, packed_buffer: u64| { - // TODO: use try block once avaliable + // TODO: use try block once available let result: Result<(WasiBuffer, Memory, Vec), Trap> = (|| { // grab a handle to the memory let plugin_memory = match caller.get_export("memory") { @@ -297,7 +297,7 @@ pub enum PluginBinary<'a> { Precompiled(&'a [u8]), } -/// Represents a WebAssembly plugin, with access to the WebAssembly System Inferface. +/// Represents a WebAssembly plugin, with access to the WebAssembly System Interface. /// Build a new plugin using [`PluginBuilder`]. pub struct Plugin { store: Store, @@ -559,7 +559,7 @@ impl Plugin { .ok_or_else(|| anyhow!("Could not grab slice of plugin memory"))?; // write the argument to linear memory - // this returns a (ptr, lentgh) pair + // this returns a (ptr, length) pair let arg_buffer = Self::bytes_to_buffer( self.store.data().alloc_buffer(), &mut plugin_memory, @@ -569,7 +569,7 @@ impl Plugin { .await?; // call the function, passing in the buffer and its length - // this returns a ptr to a (ptr, lentgh) pair + // this returns a ptr to a (ptr, length) pair let result_buffer = handle .function .call_async(&mut self.store, arg_buffer.into_u64()) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7ce07959d7..b0d0d670ba 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4052,7 +4052,7 @@ impl Project { let end_within = range.start.cmp(&primary.end, buffer).is_le() && range.end.cmp(&primary.end, buffer).is_ge(); - //Skip addtional edits which overlap with the primary completion edit + //Skip additional edits which overlap with the primary completion edit //https://github.com/zed-industries/zed/pull/1871 if !start_within && !end_within { buffer.edit([(range, text)], None, cx); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index a33a0fc050..97656b7b10 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -157,7 +157,7 @@ impl RepositoryEntry { self.statuses .iter_from(&repo_path) .take_while(|(key, _)| key.starts_with(&repo_path)) - // Short circut once we've found the highest level + // Short circuit once we've found the highest level .take_until(|(_, status)| status == &&GitFileStatus::Conflict) .map(|(_, status)| status) .reduce( @@ -3623,7 +3623,7 @@ pub trait WorktreeHandle { impl WorktreeHandle for ModelHandle { // When the worktree's FS event stream sometimes delivers "redundant" events for FS changes that - // occurred before the worktree was constructed. These events can cause the worktree to perfrom + // occurred before the worktree was constructed. These events can cause the worktree to perform // extra directory scans, and emit extra scan-state notifications. // // This function mutates the worktree's directory and waits for those mutations to be picked up, diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 71271d4e7c..0dc5dad3bf 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -276,7 +276,7 @@ mod tests { .await .unwrap(); - // Set up fake langauge server to return fuzzy matches against + // Set up fake language server to return fuzzy matches against // a fixed set of symbol names. let fake_symbols = [ symbol("one", "/external"), diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 8351cdfd2d..ca3ec7cafb 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -476,7 +476,7 @@ message Symbol { string name = 4; int32 kind = 5; string path = 6; - // Cannot use generate anchors for unopend files, + // Cannot use generate anchors for unopened files, // so we are forced to use point coords instead PointUtf16 start = 7; PointUtf16 end = 8; diff --git a/crates/rpc/src/auth.rs b/crates/rpc/src/auth.rs index 39c8c39a44..ac7bbcebec 100644 --- a/crates/rpc/src/auth.rs +++ b/crates/rpc/src/auth.rs @@ -42,7 +42,7 @@ impl PublicKey { } impl PrivateKey { - /// Decrypt a base64-encoded string that was encrypted by the correspoding public key. + /// Decrypt a base64-encoded string that was encrypted by the corresponding public key. pub fn decrypt_string(&self, encrypted_string: &str) -> Result { let encrypted_bytes = base64::decode_config(encrypted_string, base64::URL_SAFE) .context("failed to base64-decode encrypted string")?; diff --git a/crates/sqlez/src/thread_safe_connection.rs b/crates/sqlez/src/thread_safe_connection.rs index c3fe4657ee..ffadb3af41 100644 --- a/crates/sqlez/src/thread_safe_connection.rs +++ b/crates/sqlez/src/thread_safe_connection.rs @@ -160,7 +160,7 @@ impl ThreadSafeConnection { // Create a one shot channel for the result of the queued write // so we can await on the result - let (sender, reciever) = oneshot::channel(); + let (sender, receiver) = oneshot::channel(); let thread_safe_connection = (*self).clone(); write_channel(Box::new(move || { @@ -168,7 +168,7 @@ impl ThreadSafeConnection { let result = connection.with_write(|connection| callback(connection)); sender.send(result).ok(); })); - reciever.map(|response| response.expect("Write queue unexpectedly closed")) + receiver.map(|response| response.expect("Write queue unexpectedly closed")) } pub(crate) fn create_connection( @@ -245,10 +245,10 @@ pub fn background_thread_queue() -> WriteQueueConstructor { use std::sync::mpsc::channel; Box::new(|| { - let (sender, reciever) = channel::(); + let (sender, receiver) = channel::(); thread::spawn(move || { - while let Ok(write) = reciever.recv() { + while let Ok(write) = receiver.recv() { write() } }); diff --git a/crates/terminal/src/mappings/colors.rs b/crates/terminal/src/mappings/colors.rs index 5bed4b573c..3f776251b5 100644 --- a/crates/terminal/src/mappings/colors.rs +++ b/crates/terminal/src/mappings/colors.rs @@ -45,7 +45,7 @@ pub fn convert_color(alac_color: &AnsiColor, style: &TerminalStyle) -> Color { } ///Converts an 8 bit ANSI color to it's GPUI equivalent. -///Accepts usize for compatability with the alacritty::Colors interface, +///Accepts usize for compatibility with the alacritty::Colors interface, ///Other than that use case, should only be called with values in the [0,255] range pub fn get_color_at_index(index: &usize, style: &TerminalStyle) -> Color { match index { @@ -78,7 +78,7 @@ pub fn get_color_at_index(index: &usize, style: &TerminalStyle) -> Color { let step = (u8::MAX as f32 / 24.).floor() as u8; //Split the RGB grayscale values into 24 chunks Color::new(i * step, i * step, i * step, u8::MAX) //Map the ANSI-grayscale components to the RGB-grayscale } - //For compatability with the alacritty::Colors interface + //For compatibility with the alacritty::Colors interface 256 => style.foreground, 257 => style.background, 258 => style.cursor, diff --git a/crates/terminal_view/README.md b/crates/terminal_view/README.md index 272212a538..019460067e 100644 --- a/crates/terminal_view/README.md +++ b/crates/terminal_view/README.md @@ -18,6 +18,6 @@ There are currently many distinct paths for getting keystrokes to the terminal: 3. IME text. When the special character mappings fail, we pass the keystroke back to GPUI to hand it to the IME system. This comes back to us in the `View::replace_text_in_range()` method, and we then send that to the terminal directly, bypassing `try_keystroke()`. -4. Pasted text has a seperate pathway. +4. Pasted text has a separate pathway. Generally, there's a distinction between 'keystrokes that need to be mapped' and 'strings which need to be written'. I've attempted to unify these under the '.try_keystroke()' API and the `.input()` API (which try_keystroke uses) so we have consistent input handling across the terminal \ No newline at end of file diff --git a/crates/terminal_view/scripts/print256color.sh b/crates/terminal_view/scripts/print256color.sh index 99e3d8c9f9..8a53f3bc02 100755 --- a/crates/terminal_view/scripts/print256color.sh +++ b/crates/terminal_view/scripts/print256color.sh @@ -40,7 +40,7 @@ function contrast_colour { # Uncomment the below for more precise luminance calculations - # # Calculate percieved brightness + # # Calculate perceived brightness # # See https://www.w3.org/TR/AERT#color-contrast # # and http://www.itu.int/rec/R-REC-BT.601 # # Luminance is in range 0..5000 as each value is 0..5 diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 18c85db980..2f2ff2cdc3 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -34,7 +34,7 @@ use std::{mem, ops::Range}; use crate::TerminalView; -///The information generated during layout that is nescessary for painting +///The information generated during layout that is necessary for painting pub struct LayoutState { cells: Vec, rects: Vec, @@ -206,7 +206,7 @@ impl TerminalElement { //Expand background rect range { if matches!(bg, Named(NamedColor::Background)) { - //Continue to next cell, resetting variables if nescessary + //Continue to next cell, resetting variables if necessary cur_alac_color = None; if let Some(rect) = cur_rect { rects.push(rect); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 9478b3eef1..c40a1a7ccd 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -804,7 +804,7 @@ mod tests { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); - //Make sure enviroment is as expeted + //Make sure environment is as expected assert!(active_entry.is_none()); assert!(workspace.worktrees(cx).next().is_none()); @@ -825,7 +825,7 @@ mod tests { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); - //Make sure enviroment is as expeted + //Make sure environment is as expected assert!(active_entry.is_none()); assert!(workspace.worktrees(cx).next().is_some()); diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index 4b8266120a..7e26e0a296 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -193,7 +193,7 @@ fn test_line_len() { } #[test] -fn test_common_prefix_at_positionn() { +fn test_common_prefix_at_position() { let text = "a = str; b = δα"; let buffer = Buffer::new(0, 0, text.into()); @@ -216,7 +216,7 @@ fn test_common_prefix_at_positionn() { empty_range_after(text, "str"), ); - // prefix matching is case insenstive. + // prefix matching is case insensitive. assert_eq!( buffer.common_prefix_at(offset1, "Strαngε"), range_of(text, "str"), diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 83a18a1ac9..91be1022a3 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -756,7 +756,7 @@ mod test { ˇ The quick"}) .await; - // Indoc disallows trailing whitspace. + // Indoc disallows trailing whitespace. cx.assert(" ˇ \nThe quick").await; } diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index 538e83a803..c3916722dd 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -29,7 +29,7 @@ use tokio::{ use crate::state::Mode; use collections::VecDeque; -// Neovim doesn't like to be started simultaneously from multiple threads. We use thsi lock +// Neovim doesn't like to be started simultaneously from multiple threads. We use this lock // to ensure we are only constructing one neovim connection at a time. #[cfg(feature = "neovim")] lazy_static! { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 1fa8c15f9b..b784cb39b8 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1022,7 +1022,7 @@ impl Pane { let is_active_item = target_item_id == active_item_id; let target_pane = cx.weak_handle(); - // The `CloseInactiveItems` action should really be called "CloseOthers" and the behaviour should be dynamically based on the tab the action is ran on. Currenlty, this is a weird action because you can run it on a non-active tab and it will close everything by the actual active tab + // The `CloseInactiveItems` action should really be called "CloseOthers" and the behaviour should be dynamically based on the tab the action is ran on. Currently, this is a weird action because you can run it on a non-active tab and it will close everything by the actual active tab self.tab_context_menu.update(cx, |menu, cx| { menu.show( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 795747cfcc..95d0bf6538 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4424,7 +4424,7 @@ mod tests { assert!(!panel.has_focus(cx)); }); - // Transfering focus back to the panel keeps it zoomed + // Transferring focus back to the panel keeps it zoomed workspace.update(cx, |workspace, cx| { workspace.toggle_panel_focus::(cx); }); diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 429a5d9421..a25d7d02ea 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -207,7 +207,7 @@ impl LspAdapter for EsLintLspAdapter { http: Arc, ) -> Result> { // At the time of writing the latest vscode-eslint release was released in 2020 and requires - // special custom LSP protocol extensions be handled to fully initalize. Download the latest + // special custom LSP protocol extensions be handled to fully initialize. Download the latest // prerelease instead to sidestep this issue let release = latest_github_release("microsoft/vscode-eslint", true, http).await?; Ok(Box::new(GitHubLspBinaryVersion { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 6248ccf4f0..18ee4baeb2 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -1,4 +1,4 @@ -// Allow binary to be called Zed for a nice application menu when running executable direcly +// Allow binary to be called Zed for a nice application menu when running executable directly #![allow(non_snake_case)] use anyhow::{anyhow, Context, Result}; diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 9a053e9c0c..e77cb11bb9 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -23,7 +23,7 @@ function checkLicenses( licenses: string[] ) { for (const { meta } of schemeMetaWithLicense) { - // FIXME: Add support for conjuctions and conditions + // FIXME: Add support for conjunctions and conditions if (licenses.indexOf(meta.license.SPDX) < 0) { throw Error( `License for theme ${meta.name} (${meta.license.SPDX}) is not supported` diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index fa0aec2012..66074aa684 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -112,7 +112,7 @@ export default function editor(colorScheme: ColorScheme) { widthEm: 0.15, cornerRadius: 0.05, }, - /** Highlights matching occurences of what is under the cursor + /** Highlights matching occurrences of what is under the cursor * as well as matched brackets */ documentHighlightReadBackground: withOpacity( diff --git a/styles/src/styleTree/search.ts b/styles/src/styleTree/search.ts index 6fc8a95d7d..2094a2e369 100644 --- a/styles/src/styleTree/search.ts +++ b/styles/src/styleTree/search.ts @@ -33,7 +33,7 @@ export default function search(colorScheme: ColorScheme) { }; return { - // TODO: Add an activeMatchBackground on the rust side to differenciate between active and inactive + // TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive matchBackground: withOpacity(foreground(layer, "accent"), 0.4), optionButton: { ...text(layer, "mono", "on"), diff --git a/styles/src/system/types.ts b/styles/src/system/types.ts index 8bfa2dd7db..8de65a37eb 100644 --- a/styles/src/system/types.ts +++ b/styles/src/system/types.ts @@ -1,6 +1,6 @@ import { Curve } from "./ref/curves" -export interface ColorAccessiblityValue { +export interface ColorAccessibilityValue { value: number aaPass: boolean aaaPass: boolean @@ -12,14 +12,14 @@ export interface ColorAccessiblityValue { * @note This implementation is currently basic – Currently we only calculate contrasts against black and white, in the future will allow for dynamic color contrast calculation based on the colors present in a given palette. * @note The goal is to align with WCAG3 accessibility standards as they become stabilized. See the [WCAG 3 Introduction](https://www.w3.org/WAI/standards-guidelines/wcag/wcag3-intro/) for more information. */ -export interface ColorAccessiblity { - black: ColorAccessiblityValue - white: ColorAccessiblityValue +export interface ColorAccessibility { + black: ColorAccessibilityValue + white: ColorAccessibilityValue } export type Color = { step: number - contrast: ColorAccessiblity + contrast: ColorAccessibility hex: string lch: number[] rgba: number[] From 5790d6993e79b8200c14d2ac0a7cbd2cda54b670 Mon Sep 17 00:00:00 2001 From: Julia Date: Sun, 4 Jun 2023 23:34:22 -0400 Subject: [PATCH 070/131] Update rust-bindgen dev-dependencies --- Cargo.lock | 113 ++++++++++++++-------------------------- crates/gpui/Cargo.toml | 2 +- crates/media/Cargo.toml | 2 +- 3 files changed, 41 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07a859b058..7f9a553013 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,15 +180,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "anyhow" version = "1.0.71" @@ -404,7 +395,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -452,7 +443,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -495,7 +486,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -715,24 +706,24 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.59.2" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap 2.34.0", - "env_logger 0.9.3", "lazy_static", "lazycell", "log", "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", + "syn 2.0.18", "which", ] @@ -1085,21 +1076,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap 0.11.0", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "3.2.25" @@ -1112,9 +1088,9 @@ dependencies = [ "clap_lex", "indexmap", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", - "textwrap 0.16.0", + "textwrap", ] [[package]] @@ -1144,7 +1120,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 3.2.25", + "clap", "core-foundation", "core-services", "dirs 3.0.2", @@ -1254,7 +1230,7 @@ dependencies = [ "axum-extra", "base64 0.13.1", "call", - "clap 3.2.25", + "clap", "client", "collections", "ctor", @@ -1797,7 +1773,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -1814,7 +1790,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -2587,7 +2563,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4348,7 +4324,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4793,6 +4769,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +dependencies = [ + "proc-macro2", + "syn 2.0.18", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -4828,9 +4814,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -5107,9 +5093,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -6053,7 +6039,7 @@ checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -6096,7 +6082,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -6583,12 +6569,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -6660,9 +6640,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -6848,15 +6828,6 @@ dependencies = [ "util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "textwrap" version = "0.16.0" @@ -6930,7 +6901,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -7088,7 +7059,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -7276,7 +7247,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -7837,12 +7808,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -8006,7 +7971,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "wasm-bindgen-shared", ] @@ -8040,7 +8005,7 @@ checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8907,7 +8872,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index c1dc13084e..31b7db008d 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -53,7 +53,7 @@ uuid = { version = "1.1.2", features = ["v4"] } waker-fn = "1.1.0" [build-dependencies] -bindgen = "0.59.2" +bindgen = "0.65.1" cc = "1.0.67" [dev-dependencies] diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index 22a494d754..3825897e7b 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -18,4 +18,4 @@ metal = "0.21.0" objc = "0.2" [build-dependencies] -bindgen = "0.59.2" +bindgen = "0.65.1" From bef6932da794877ab1011d1b3cacf28a9e4d17d0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 5 Jun 2023 11:25:21 +0200 Subject: [PATCH 071/131] Avoid accidentally taking the `api_key` when requesting an assist --- crates/ai/src/ai.rs | 4 +--- crates/ai/src/assistant.rs | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index a5d5666a72..11704de03e 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -2,11 +2,9 @@ pub mod assistant; mod assistant_settings; pub use assistant::AssistantPanel; -use gpui::{actions, AppContext}; +use gpui::AppContext; use serde::{Deserialize, Serialize}; -actions!(ai, [Assist]); - // Data types for chat completion requests #[derive(Serialize)] struct OpenAIRequest { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 68f722f1ee..7020f9f38c 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -16,7 +16,7 @@ use gpui::{ use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use settings::SettingsStore; -use std::{cell::Cell, io, rc::Rc, sync::Arc, time::Duration}; +use std::{cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; use tiktoken_rs::model::get_context_size; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ @@ -62,7 +62,7 @@ pub struct AssistantPanel { width: Option, height: Option, pane: ViewHandle, - api_key: Rc>>, + api_key: Rc>>, api_key_editor: Option>, has_read_credentials: bool, languages: Arc, @@ -136,7 +136,7 @@ impl AssistantPanel { let mut this = Self { pane, - api_key: Rc::new(Cell::new(None)), + api_key: Rc::new(RefCell::new(None)), api_key_editor: None, has_read_credentials: false, languages: workspace.app_state().languages.clone(), @@ -199,7 +199,7 @@ impl AssistantPanel { cx.platform() .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) .log_err(); - self.api_key.set(Some(api_key)); + *self.api_key.borrow_mut() = Some(api_key); self.api_key_editor.take(); cx.focus_self(); cx.notify(); @@ -333,7 +333,7 @@ impl Panel for AssistantPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { if active { - if self.api_key.clone().take().is_none() && !self.has_read_credentials { + if self.api_key.borrow().is_none() && !self.has_read_credentials { self.has_read_credentials = true; let api_key = if let Some((_, api_key)) = cx .platform() @@ -346,7 +346,7 @@ impl Panel for AssistantPanel { None }; if let Some(api_key) = api_key { - self.api_key.set(Some(api_key)); + *self.api_key.borrow_mut() = Some(api_key); } else if self.api_key_editor.is_none() { self.api_key_editor = Some(build_api_key_editor(cx)); cx.notify(); @@ -403,7 +403,7 @@ struct Assistant { token_count: Option, max_token_count: usize, pending_token_count: Task>, - api_key: Rc>>, + api_key: Rc>>, _subscriptions: Vec, } @@ -413,7 +413,7 @@ impl Entity for Assistant { impl Assistant { fn new( - api_key: Rc>>, + api_key: Rc>>, language_registry: Arc, cx: &mut ModelContext, ) -> Self { @@ -504,7 +504,8 @@ impl Assistant { stream: true, }; - if let Some(api_key) = self.api_key.clone().take() { + let api_key = self.api_key.borrow().clone(); + if let Some(api_key) = api_key { let stream = stream_completion(api_key, cx.background().clone(), request); let response = self.push_message(Role::Assistant, cx); self.push_message(Role::User, cx); @@ -600,7 +601,7 @@ struct AssistantEditor { impl AssistantEditor { fn new( - api_key: Rc>>, + api_key: Rc>>, language_registry: Arc, cx: &mut ViewContext, ) -> Self { @@ -846,7 +847,9 @@ async fn stream_completion( while let Some(line) = lines.next().await { if let Some(event) = parse_line(line).transpose() { - tx.unbounded_send(event).log_err(); + if tx.unbounded_send(event).is_err() { + break; + } } } From c872f581d14ab26b113ae25a0f930edd8ca0c700 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 5 Jun 2023 12:00:40 +0200 Subject: [PATCH 072/131] Query `rect_for_text_range` on focused view instead of root element This was causing IME input to be drawn in the wrong place when there were splits or panels in the window. --- crates/gpui/src/app/window.rs | 4 ++-- crates/gpui/src/app/window_input_handler.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 30d58cdd72..cfcef626df 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -965,10 +965,10 @@ impl<'a> WindowContext<'a> { } pub fn rect_for_text_range(&self, range_utf16: Range) -> Option { - let root_view_id = self.window.root_view().id(); + let focused_view_id = self.window.focused_view_id?; self.window .rendered_views - .get(&root_view_id)? + .get(&focused_view_id)? .rect_for_text_range(range_utf16, self) .log_err() .flatten() diff --git a/crates/gpui/src/app/window_input_handler.rs b/crates/gpui/src/app/window_input_handler.rs index 938dbe1a03..8ee9f7eeff 100644 --- a/crates/gpui/src/app/window_input_handler.rs +++ b/crates/gpui/src/app/window_input_handler.rs @@ -84,8 +84,8 @@ impl InputHandler for WindowInputHandler { fn rect_for_range(&self, range_utf16: Range) -> Option { self.app - .borrow_mut() - .update_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16)) + .borrow() + .read_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16)) .flatten() } } From 917d8949b78c2c1a704b0882dd79fa79310ce6ef Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Mon, 5 Jun 2023 14:17:48 -0400 Subject: [PATCH 073/131] Move app version into panic object --- crates/zed/src/main.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 18ee4baeb2..a3c359c189 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -372,6 +372,7 @@ struct Panic { #[serde(skip_serializing_if = "Option::is_none")] location_data: Option, backtrace: Vec, + app_version: String, release_channel: String, os_name: String, os_version: Option, @@ -383,8 +384,6 @@ struct Panic { #[derive(Serialize)] struct PanicRequest { panic: Panic, - // TODO: Move to Panic struct, as app_version - requires changing zed.dev - version: String, token: String, } @@ -471,6 +470,7 @@ fn init_panic_hook(app: &App) { file: location.file().into(), line: location.line(), }), + app_version: app_version.clone(), release_channel: RELEASE_CHANNEL.dev_name().into(), os_name: platform.os_name().into(), os_version: platform @@ -496,8 +496,7 @@ fn init_panic_hook(app: &App) { } let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); - let panic_file_path = - paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, timestamp)); + let panic_file_path = paths::LOGS_DIR.join(format!("zed-{}.panic", timestamp)); let panic_file = std::fs::OpenOptions::new() .append(true) .create(true) @@ -532,15 +531,9 @@ fn upload_previous_panics(http: Arc, cx: &mut AppContext) { continue; }; - let mut components = filename.split('-'); - if components.next() != Some("zed") { + if !filename.starts_with("zed") { continue; } - let version = if let Some(version) = components.next() { - version - } else { - continue; - }; if telemetry_settings.diagnostics { let panic_data_text = smol::fs::read_to_string(&child_path) @@ -549,7 +542,6 @@ fn upload_previous_panics(http: Arc, cx: &mut AppContext) { let body = serde_json::to_string(&PanicRequest { panic: serde_json::from_str(&panic_data_text)?, - version: version.to_string(), token: ZED_SECRET_CLIENT_TOKEN.into(), }) .unwrap(); From 70c5489c1345b4f53a0fe603356e9aeffb10337a Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Mon, 5 Jun 2023 15:09:47 -0400 Subject: [PATCH 074/131] Stop sending editor events to mixpanel --- crates/editor/src/editor.rs | 15 +++------------ crates/editor/src/editor_tests.rs | 3 ++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d98bd92532..f956d5725c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -46,9 +46,9 @@ use gpui::{ impl_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton}, - serde_json::{self, json}, - AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, - ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, + Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -7211,15 +7211,6 @@ impl Editor { .show_copilot_suggestions; let telemetry = project.read(cx).client().telemetry().clone(); - telemetry.report_mixpanel_event( - match name { - "open" => "open editor", - "save" => "save editor", - _ => name, - }, - json!({ "File Extension": file_extension, "Vim Mode": vim_mode, "In Clickhouse": true }), - telemetry_settings, - ); let event = ClickhouseEvent::Editor { file_extension, vim_mode, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 35d61bf0cf..a9d100575f 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -9,7 +9,8 @@ use gpui::{ executor::Deterministic, geometry::{rect::RectF, vector::vec2f}, platform::{WindowBounds, WindowOptions}, - serde_json, TestAppContext, + serde_json::{self, json}, + TestAppContext, }; use indoc::indoc; use language::{ From 311074e3977f9918ec2b567af6f84702024817a1 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Mon, 5 Jun 2023 15:33:17 -0400 Subject: [PATCH 075/131] Remove code sending zed events to mixpanel --- .github/workflows/ci.yml | 1 - .github/workflows/release_actions.yml | 16 -- crates/client/src/client.rs | 18 -- crates/client/src/telemetry.rs | 202 +---------------------- crates/zed/build.rs | 3 - crates/zed/src/main.rs | 5 - script/mixpanel_release/main.py | 30 ---- script/mixpanel_release/requirements.txt | 1 - 8 files changed, 4 insertions(+), 272 deletions(-) delete mode 100644 script/mixpanel_release/main.py delete mode 100644 script/mixpanel_release/requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cd717d5c1..f1c16b2d4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,6 @@ jobs: MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }} APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} - ZED_MIXPANEL_TOKEN: ${{ secrets.ZED_MIXPANEL_TOKEN }} steps: - name: Install Rust run: | diff --git a/.github/workflows/release_actions.yml b/.github/workflows/release_actions.yml index 5feb29e469..4ccab09cbe 100644 --- a/.github/workflows/release_actions.yml +++ b/.github/workflows/release_actions.yml @@ -21,19 +21,3 @@ jobs: ${{ github.event.release.body }} ``` - mixpanel_release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: "3.10.5" - architecture: "x64" - cache: "pip" - - run: pip install -r script/mixpanel_release/requirements.txt - - run: > - python script/mixpanel_release/main.py - ${{ github.event.release.tag_name }} - ${{ secrets.MIXPANEL_PROJECT_ID }} - ${{ secrets.MIXPANEL_SERVICE_ACCOUNT_USERNAME }} - ${{ secrets.MIXPANEL_SERVICE_ACCOUNT_SECRET }} diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index c9b83d805a..78bcc55e93 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -776,15 +776,6 @@ impl Client { if credentials.is_none() && try_keychain { credentials = read_credentials_from_keychain(cx); read_from_keychain = credentials.is_some(); - if read_from_keychain { - cx.read(|cx| { - self.telemetry().report_mixpanel_event( - "read credentials from keychain", - Default::default(), - *settings::get::(cx), - ); - }); - } } if credentials.is_none() { let mut status_rx = self.status(); @@ -1072,11 +1063,8 @@ impl Client { ) -> Task> { let platform = cx.platform(); let executor = cx.background(); - let telemetry = self.telemetry.clone(); let http = self.http.clone(); - let telemetry_settings = cx.read(|cx| *settings::get::(cx)); - executor.clone().spawn(async move { // Generate a pair of asymmetric encryption keys. The public key will be used by the // zed server to encrypt the user's access token, so that it can'be intercepted by @@ -1159,12 +1147,6 @@ impl Client { .context("failed to decrypt access token")?; platform.activate(true); - telemetry.report_mixpanel_event( - "authenticate with browser", - Default::default(), - telemetry_settings, - ); - Ok(Credentials { user_id: user_id.parse()?, access_token, diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index cf47700dfe..c1d5822246 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -1,14 +1,9 @@ use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; use db::kvp::KEY_VALUE_STORE; -use gpui::{ - executor::Background, - serde_json::{self, value::Map, Value}, - AppContext, Task, -}; +use gpui::{executor::Background, serde_json, AppContext, Task}; use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Serialize; -use serde_json::json; use std::{ env, io::Write, @@ -19,7 +14,7 @@ use std::{ }; use tempfile::NamedTempFile; use util::http::HttpClient; -use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt}; +use util::{channel::ReleaseChannel, TryFutureExt}; use uuid::Uuid; pub struct Telemetry { @@ -37,23 +32,15 @@ struct TelemetryState { os_name: &'static str, os_version: Option>, architecture: &'static str, - mixpanel_events_queue: Vec, clickhouse_events_queue: Vec, - next_mixpanel_event_id: usize, - flush_mixpanel_events_task: Option>, flush_clickhouse_events_task: Option>, log_file: Option, is_staff: Option, } -const MIXPANEL_EVENTS_URL: &'static str = "https://api.mixpanel.com/track"; -const MIXPANEL_ENGAGE_URL: &'static str = "https://api.mixpanel.com/engage#profile-set"; const CLICKHOUSE_EVENTS_URL_PATH: &'static str = "/api/events"; lazy_static! { - static ref MIXPANEL_TOKEN: Option = std::env::var("ZED_MIXPANEL_TOKEN") - .ok() - .or_else(|| option_env!("ZED_MIXPANEL_TOKEN").map(|key| key.to_string())); static ref CLICKHOUSE_EVENTS_URL: String = format!("{}{}", *ZED_SERVER_URL, CLICKHOUSE_EVENTS_URL_PATH); } @@ -95,47 +82,6 @@ pub enum ClickhouseEvent { }, } -#[derive(Serialize, Debug)] -struct MixpanelEvent { - event: String, - properties: MixpanelEventProperties, -} - -#[derive(Serialize, Debug)] -struct MixpanelEventProperties { - // Mixpanel required fields - #[serde(skip_serializing_if = "str::is_empty")] - token: &'static str, - time: u128, - #[serde(rename = "distinct_id")] - installation_id: Option>, - #[serde(rename = "$insert_id")] - insert_id: usize, - // Custom fields - #[serde(skip_serializing_if = "Option::is_none", flatten)] - event_properties: Option>, - #[serde(rename = "OS Name")] - os_name: &'static str, - #[serde(rename = "OS Version")] - os_version: Option>, - #[serde(rename = "Release Channel")] - release_channel: Option<&'static str>, - #[serde(rename = "App Version")] - app_version: Option>, - #[serde(rename = "Signed In")] - signed_in: bool, -} - -#[derive(Serialize)] -struct MixpanelEngageRequest { - #[serde(rename = "$token")] - token: &'static str, - #[serde(rename = "$distinct_id")] - installation_id: Arc, - #[serde(rename = "$set")] - set: Value, -} - #[cfg(debug_assertions)] const MAX_QUEUE_LEN: usize = 1; @@ -168,29 +114,13 @@ impl Telemetry { release_channel, installation_id: None, metrics_id: None, - mixpanel_events_queue: Default::default(), clickhouse_events_queue: Default::default(), - flush_mixpanel_events_task: Default::default(), flush_clickhouse_events_task: Default::default(), - next_mixpanel_event_id: 0, log_file: None, is_staff: None, }), }); - if MIXPANEL_TOKEN.is_some() { - this.executor - .spawn({ - let this = this.clone(); - async move { - if let Some(tempfile) = NamedTempFile::new().log_err() { - this.state.lock().log_file = Some(tempfile); - } - } - }) - .detach(); - } - this } @@ -218,20 +148,9 @@ impl Telemetry { let mut state = this.state.lock(); state.installation_id = Some(installation_id.clone()); - for event in &mut state.mixpanel_events_queue { - event - .properties - .installation_id - .get_or_insert_with(|| installation_id.clone()); - } - - let has_mixpanel_events = !state.mixpanel_events_queue.is_empty(); let has_clickhouse_events = !state.clickhouse_events_queue.is_empty(); - drop(state); - if has_mixpanel_events { - this.flush_mixpanel_events(); - } + drop(state); if has_clickhouse_events { this.flush_clickhouse_events(); @@ -256,37 +175,11 @@ impl Telemetry { return; } - let this = self.clone(); let mut state = self.state.lock(); - let installation_id = state.installation_id.clone(); let metrics_id: Option> = metrics_id.map(|id| id.into()); state.metrics_id = metrics_id.clone(); state.is_staff = Some(is_staff); drop(state); - - if let Some((token, installation_id)) = MIXPANEL_TOKEN.as_ref().zip(installation_id) { - self.executor - .spawn( - async move { - let json_bytes = serde_json::to_vec(&[MixpanelEngageRequest { - token, - installation_id, - set: json!({ - "Staff": is_staff, - "ID": metrics_id, - "App": true - }), - }])?; - - this.http_client - .post_json(MIXPANEL_ENGAGE_URL, json_bytes.into()) - .await?; - anyhow::Ok(()) - } - .log_err(), - ) - .detach(); - } } pub fn report_clickhouse_event( @@ -310,7 +203,7 @@ impl Telemetry { }); if state.installation_id.is_some() { - if state.mixpanel_events_queue.len() >= MAX_QUEUE_LEN { + if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { drop(state); self.flush_clickhouse_events(); } else { @@ -324,55 +217,6 @@ impl Telemetry { } } - pub fn report_mixpanel_event( - self: &Arc, - kind: &str, - properties: Value, - telemetry_settings: TelemetrySettings, - ) { - if !telemetry_settings.metrics { - return; - } - - let mut state = self.state.lock(); - let event = MixpanelEvent { - event: kind.into(), - properties: MixpanelEventProperties { - token: "", - time: SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis(), - installation_id: state.installation_id.clone(), - insert_id: post_inc(&mut state.next_mixpanel_event_id), - event_properties: if let Value::Object(properties) = properties { - Some(properties) - } else { - None - }, - os_name: state.os_name, - os_version: state.os_version.clone(), - release_channel: state.release_channel, - app_version: state.app_version.clone(), - signed_in: state.metrics_id.is_some(), - }, - }; - state.mixpanel_events_queue.push(event); - if state.installation_id.is_some() { - if state.mixpanel_events_queue.len() >= MAX_QUEUE_LEN { - drop(state); - self.flush_mixpanel_events(); - } else { - let this = self.clone(); - let executor = self.executor.clone(); - state.flush_mixpanel_events_task = Some(self.executor.spawn(async move { - executor.timer(DEBOUNCE_INTERVAL).await; - this.flush_mixpanel_events(); - })); - } - } - } - pub fn metrics_id(self: &Arc) -> Option> { self.state.lock().metrics_id.clone() } @@ -385,44 +229,6 @@ impl Telemetry { self.state.lock().is_staff } - fn flush_mixpanel_events(self: &Arc) { - let mut state = self.state.lock(); - let mut events = mem::take(&mut state.mixpanel_events_queue); - state.flush_mixpanel_events_task.take(); - drop(state); - - if let Some(token) = MIXPANEL_TOKEN.as_ref() { - let this = self.clone(); - self.executor - .spawn( - async move { - let mut json_bytes = Vec::new(); - - if let Some(file) = &mut this.state.lock().log_file { - let file = file.as_file_mut(); - for event in &mut events { - json_bytes.clear(); - serde_json::to_writer(&mut json_bytes, event)?; - file.write_all(&json_bytes)?; - file.write(b"\n")?; - - event.properties.token = token; - } - } - - json_bytes.clear(); - serde_json::to_writer(&mut json_bytes, &events)?; - this.http_client - .post_json(MIXPANEL_EVENTS_URL, json_bytes.into()) - .await?; - anyhow::Ok(()) - } - .log_err(), - ) - .detach(); - } - } - fn flush_clickhouse_events(self: &Arc) { let mut state = self.state.lock(); let mut events = mem::take(&mut state.clickhouse_events_queue); diff --git a/crates/zed/build.rs b/crates/zed/build.rs index bb971041a5..14bf9999fb 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -1,9 +1,6 @@ fn main() { println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); - if let Ok(value) = std::env::var("ZED_MIXPANEL_TOKEN") { - println!("cargo:rustc-env=ZED_MIXPANEL_TOKEN={value}"); - } if let Ok(value) = std::env::var("ZED_PREVIEW_CHANNEL") { println!("cargo:rustc-env=ZED_PREVIEW_CHANNEL={value}"); } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index a3c359c189..59072cc9f4 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -171,11 +171,6 @@ fn main() { .detach(); client.telemetry().start(); - client.telemetry().report_mixpanel_event( - "start app", - Default::default(), - *settings::get::(cx), - ); let app_state = Arc::new(AppState { languages, diff --git a/script/mixpanel_release/main.py b/script/mixpanel_release/main.py deleted file mode 100644 index e2a0eeb990..0000000000 --- a/script/mixpanel_release/main.py +++ /dev/null @@ -1,30 +0,0 @@ -import datetime -import sys -import requests - -def main(): - version = sys.argv[1] - version = version.removeprefix("v") - project_id = sys.argv[2] - account_username = sys.argv[3] - account_secret = sys.argv[4] - - current_datetime = datetime.datetime.now(datetime.timezone.utc) - current_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S") - - url = f"https://mixpanel.com/api/app/projects/{project_id}/annotations" - - payload = { - "date": current_datetime, - "description": version - } - - response = requests.post( - url, - auth=(account_username, account_secret), - json=payload - ) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/script/mixpanel_release/requirements.txt b/script/mixpanel_release/requirements.txt deleted file mode 100644 index 5e77405687..0000000000 --- a/script/mixpanel_release/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.28.1 \ No newline at end of file From 624467ebca0bc9e7aae684b9ecfb39fed7d1539a Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 5 Jun 2023 12:53:37 -0700 Subject: [PATCH 076/131] Add file and line number information to logs --- crates/util/src/util.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 9d787e1389..5f738132ae 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -9,6 +9,7 @@ pub mod test; use std::{ cmp::{self, Ordering}, ops::{AddAssign, Range, RangeInclusive}, + panic::Location, pin::Pin, task::{Context, Poll}, }; @@ -129,11 +130,13 @@ where { type Ok = T; + #[track_caller] fn log_err(self) -> Option { match self { Ok(value) => Some(value), Err(error) => { - log::error!("{:?}", error); + let caller = Location::caller(); + log::error!("{}:{}: {:?}", caller.file(), caller.line(), error); None } } From 12dd91c89ce317774de0ddb58c82b28c4cae5b13 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Jun 2023 14:12:19 -0700 Subject: [PATCH 077/131] Use local ids, not remote ids, to identify buffers to copilot --- crates/copilot/src/copilot.rs | 47 +++++++++++++++-------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 04e8764f7b..e73424f0cd 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -4,7 +4,7 @@ mod sign_in; use anyhow::{anyhow, Context, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; -use collections::HashMap; +use collections::{HashMap, HashSet}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use gpui::{ actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle, @@ -127,7 +127,7 @@ impl CopilotServer { struct RunningCopilotServer { lsp: Arc, sign_in_status: SignInStatus, - registered_buffers: HashMap, + registered_buffers: HashMap, } #[derive(Clone, Debug)] @@ -163,7 +163,6 @@ impl Status { } struct RegisteredBuffer { - id: u64, uri: lsp::Url, language_id: String, snapshot: BufferSnapshot, @@ -178,13 +177,13 @@ impl RegisteredBuffer { buffer: &ModelHandle, cx: &mut ModelContext, ) -> oneshot::Receiver<(i32, BufferSnapshot)> { - let id = self.id; let (done_tx, done_rx) = oneshot::channel(); if buffer.read(cx).version() == self.snapshot.version { let _ = done_tx.send((self.snapshot_version, self.snapshot.clone())); } else { let buffer = buffer.downgrade(); + let id = buffer.id(); let prev_pending_change = mem::replace(&mut self.pending_buffer_change, Task::ready(None)); self.pending_buffer_change = cx.spawn_weak(|copilot, mut cx| async move { @@ -268,7 +267,7 @@ pub struct Copilot { http: Arc, node_runtime: Arc, server: CopilotServer, - buffers: HashMap>, + buffers: HashSet>, } impl Entity for Copilot { @@ -559,8 +558,8 @@ impl Copilot { } pub fn register_buffer(&mut self, buffer: &ModelHandle, cx: &mut ModelContext) { - let buffer_id = buffer.read(cx).remote_id(); - self.buffers.insert(buffer_id, buffer.downgrade()); + let weak_buffer = buffer.downgrade(); + self.buffers.insert(weak_buffer.clone()); if let CopilotServer::Running(RunningCopilotServer { lsp: server, @@ -573,8 +572,7 @@ impl Copilot { return; } - let buffer_id = buffer.read(cx).remote_id(); - registered_buffers.entry(buffer_id).or_insert_with(|| { + registered_buffers.entry(buffer.id()).or_insert_with(|| { let uri: lsp::Url = uri_for_buffer(buffer, cx); let language_id = id_for_language(buffer.read(cx).language()); let snapshot = buffer.read(cx).snapshot(); @@ -592,7 +590,6 @@ impl Copilot { .log_err(); RegisteredBuffer { - id: buffer_id, uri, language_id, snapshot, @@ -603,8 +600,8 @@ impl Copilot { this.handle_buffer_event(buffer, event, cx).log_err(); }), cx.observe_release(buffer, move |this, _buffer, _cx| { - this.buffers.remove(&buffer_id); - this.unregister_buffer(buffer_id); + this.buffers.remove(&weak_buffer); + this.unregister_buffer(&weak_buffer); }), ], } @@ -619,8 +616,7 @@ impl Copilot { cx: &mut ModelContext, ) -> Result<()> { if let Ok(server) = self.server.as_running() { - let buffer_id = buffer.read(cx).remote_id(); - if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer_id) { + if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) { match event { language::Event::Edited => { let _ = registered_buffer.report_changes(&buffer, cx); @@ -674,9 +670,9 @@ impl Copilot { Ok(()) } - fn unregister_buffer(&mut self, buffer_id: u64) { + fn unregister_buffer(&mut self, buffer: &WeakModelHandle) { if let Ok(server) = self.server.as_running() { - if let Some(buffer) = server.registered_buffers.remove(&buffer_id) { + if let Some(buffer) = server.registered_buffers.remove(&buffer.id()) { server .lsp .notify::( @@ -779,8 +775,7 @@ impl Copilot { Err(error) => return Task::ready(Err(error)), }; let lsp = server.lsp.clone(); - let buffer_id = buffer.read(cx).remote_id(); - let registered_buffer = server.registered_buffers.get_mut(&buffer_id).unwrap(); + let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap(); let snapshot = registered_buffer.report_changes(buffer, cx); let buffer = buffer.read(cx); let uri = registered_buffer.uri.clone(); @@ -850,7 +845,7 @@ impl Copilot { lsp_status: request::SignInStatus, cx: &mut ModelContext, ) { - self.buffers.retain(|_, buffer| buffer.is_upgradable(cx)); + self.buffers.retain(|buffer| buffer.is_upgradable(cx)); if let Ok(server) = self.server.as_running() { match lsp_status { @@ -858,7 +853,7 @@ impl Copilot { | request::SignInStatus::MaybeOk { .. } | request::SignInStatus::AlreadySignedIn { .. } => { server.sign_in_status = SignInStatus::Authorized; - for buffer in self.buffers.values().cloned().collect::>() { + for buffer in self.buffers.iter().cloned().collect::>() { if let Some(buffer) = buffer.upgrade(cx) { self.register_buffer(&buffer, cx); } @@ -866,14 +861,14 @@ impl Copilot { } request::SignInStatus::NotAuthorized { .. } => { server.sign_in_status = SignInStatus::Unauthorized; - for buffer_id in self.buffers.keys().copied().collect::>() { - self.unregister_buffer(buffer_id); + for buffer in self.buffers.iter().copied().collect::>() { + self.unregister_buffer(&buffer); } } request::SignInStatus::NotSignedIn => { server.sign_in_status = SignInStatus::SignedOut; - for buffer_id in self.buffers.keys().copied().collect::>() { - self.unregister_buffer(buffer_id); + for buffer in self.buffers.iter().copied().collect::>() { + self.unregister_buffer(&buffer); } } } @@ -896,9 +891,7 @@ fn uri_for_buffer(buffer: &ModelHandle, cx: &AppContext) -> lsp::Url { if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) { lsp::Url::from_file_path(file.abs_path(cx)).unwrap() } else { - format!("buffer://{}", buffer.read(cx).remote_id()) - .parse() - .unwrap() + format!("buffer://{}", buffer.id()).parse().unwrap() } } From 3d1ba1b36365990bd2743a347b15f81ced8387d5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Jun 2023 14:36:21 -0700 Subject: [PATCH 078/131] Apply bounds to all windows when using start-local-collaboration script --- crates/workspace/src/workspace.rs | 53 +++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 95d0bf6538..a429a30a39 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -765,25 +765,21 @@ impl Workspace { DB.next_id().await.unwrap_or(0) }; - let window_bounds_override = - ZED_WINDOW_POSITION - .zip(*ZED_WINDOW_SIZE) - .map(|(position, size)| { - WindowBounds::Fixed(RectF::new( - cx.platform().screens()[0].bounds().origin() + position, - size, - )) - }); - - let build_workspace = |cx: &mut ViewContext| { - Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) - }; - let workspace = requesting_window_id .and_then(|window_id| { - cx.update(|cx| cx.replace_root_view(window_id, |cx| build_workspace(cx))) + cx.update(|cx| { + cx.replace_root_view(window_id, |cx| { + Workspace::new( + workspace_id, + project_handle.clone(), + app_state.clone(), + cx, + ) + }) + }) }) .unwrap_or_else(|| { + let window_bounds_override = window_bounds_env_override(&cx); let (bounds, display) = if let Some(bounds) = window_bounds_override { (Some(bounds), None) } else { @@ -819,7 +815,14 @@ impl Workspace { // Use the serialized workspace to construct the new window cx.add_window( (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), - |cx| build_workspace(cx), + |cx| { + Workspace::new( + workspace_id, + project_handle.clone(), + app_state.clone(), + cx, + ) + }, ) .1 }); @@ -3110,6 +3113,17 @@ impl Workspace { } } +fn window_bounds_env_override(cx: &AsyncAppContext) -> Option { + ZED_WINDOW_POSITION + .zip(*ZED_WINDOW_SIZE) + .map(|(position, size)| { + WindowBounds::Fixed(RectF::new( + cx.platform().screens()[0].bounds().origin() + position, + size, + )) + }) +} + async fn open_items( serialized_workspace: Option, workspace: &WeakViewHandle, @@ -3642,8 +3656,13 @@ pub fn join_remote_project( }) .await?; + let window_bounds_override = window_bounds_env_override(&cx); let (_, workspace) = cx.add_window( - (app_state.build_window_options)(None, None, cx.platform().as_ref()), + (app_state.build_window_options)( + window_bounds_override, + None, + cx.platform().as_ref(), + ), |cx| Workspace::new(0, project, app_state.clone(), cx), ); (app_state.initialize_workspace)( From 0949ee84d8aa72872455f1c7f38c468b1e83646b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Jun 2023 15:02:29 -0700 Subject: [PATCH 079/131] :art: Move OpenSettings action back to the zed crate --- crates/workspace/src/workspace.rs | 16 +--------------- crates/zed/src/main.rs | 6 ++---- crates/zed/src/menus.rs | 2 +- crates/zed/src/zed.rs | 11 +++++++++++ 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a429a30a39..278b02d74e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -15,7 +15,6 @@ mod toolbar; mod workspace_settings; use anyhow::{anyhow, Context, Result}; -use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, @@ -83,7 +82,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::{Theme, ThemeSettings}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{async_iife, paths, ResultExt}; +use util::{async_iife, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; lazy_static! { @@ -133,8 +132,6 @@ actions!( ] ); -actions!(zed, [OpenSettings]); - #[derive(Clone, PartialEq)] pub struct OpenPaths { pub paths: Vec, @@ -295,17 +292,6 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { .detach(); }); - cx.add_action( - move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { - create_and_open_local_file(&paths::SETTINGS, cx, || { - settings::initial_user_settings_content(&Assets) - .as_ref() - .into() - }) - .detach_and_log_err(cx); - }, - ); - let client = &app_state.client; client.add_view_request_handler(Workspace::handle_follow); client.add_view_message_handler(Workspace::handle_unfollow); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 59072cc9f4..f5031beb2e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -56,9 +56,7 @@ use fs::RealFs; #[cfg(debug_assertions)] use staff_mode::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; -use workspace::{ - item::ItemHandle, notifications::NotifyResultExt, AppState, OpenSettings, Workspace, -}; +use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace}; use zed::{ self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus, }; @@ -877,6 +875,6 @@ pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { ("Go to file", &file_finder::Toggle), ("Open command palette", &command_palette::Toggle), ("Open recent projects", &recent_projects::OpenRecent), - ("Change your settings", &OpenSettings), + ("Change your settings", &zed::OpenSettings), ] } diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 76e88325f5..83af92e264 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -12,7 +12,7 @@ pub fn menus() -> Vec> { MenuItem::submenu(Menu { name: "Preferences", items: vec![ - MenuItem::action("Open Settings", workspace::OpenSettings), + MenuItem::action("Open Settings", super::OpenSettings), MenuItem::action("Open Key Bindings", super::OpenKeymap), MenuItem::action("Open Default Settings", super::OpenDefaultSettings), MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6dbddae2ad..7472500d39 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -65,6 +65,7 @@ actions!( OpenLicenses, OpenTelemetryLog, OpenKeymap, + OpenSettings, OpenDefaultSettings, OpenDefaultKeymap, IncreaseBufferFontSize, @@ -157,6 +158,16 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { create_and_open_local_file(&paths::KEYMAP, cx, Default::default).detach_and_log_err(cx); }, ); + cx.add_action( + move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { + create_and_open_local_file(&paths::SETTINGS, cx, || { + settings::initial_user_settings_content(&Assets) + .as_ref() + .into() + }) + .detach_and_log_err(cx); + }, + ); cx.add_action( move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( From cb975f125255713bc07b60c1d21d531f1c4e0011 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Jun 2023 17:45:42 -0700 Subject: [PATCH 080/131] Add Zed > Preferences > Local Settings to application menu --- crates/zed/src/menus.rs | 1 + crates/zed/src/zed.rs | 55 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 83af92e264..9112cd207b 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -16,6 +16,7 @@ pub fn menus() -> Vec> { MenuItem::action("Open Key Bindings", super::OpenKeymap), MenuItem::action("Open Default Settings", super::OpenDefaultSettings), MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap), + MenuItem::action("Open Local Settings", super::OpenLocalSettings), MenuItem::action("Select Theme", theme_selector::Toggle), ], }), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 7472500d39..ae9370384a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -33,7 +33,11 @@ use serde_json::to_string_pretty; use settings::{KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_panel::{self, TerminalPanel}; -use util::{channel::ReleaseChannel, paths, ResultExt}; +use util::{ + channel::ReleaseChannel, + paths::{self, LOCAL_SETTINGS_RELATIVE_PATH}, + ResultExt, +}; use uuid::Uuid; use welcome::BaseKeymap; pub use workspace; @@ -66,6 +70,7 @@ actions!( OpenTelemetryLog, OpenKeymap, OpenSettings, + OpenLocalSettings, OpenDefaultSettings, OpenDefaultKeymap, IncreaseBufferFontSize, @@ -168,6 +173,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { .detach_and_log_err(cx); }, ); + cx.add_action(open_local_settings_file); cx.add_action( move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( @@ -555,6 +561,53 @@ pub fn handle_keymap_file_changes( .detach(); } +fn open_local_settings_file( + workspace: &mut Workspace, + _: &OpenLocalSettings, + cx: &mut ViewContext, +) { + let project = workspace.project().clone(); + let worktree = project + .read(cx) + .visible_worktrees(cx) + .find_map(|tree| tree.read(cx).root_entry()?.is_dir().then_some(tree)); + if let Some(worktree) = worktree { + let tree_id = worktree.read(cx).id(); + cx.spawn(|workspace, mut cx| async move { + let file_path = &*LOCAL_SETTINGS_RELATIVE_PATH; + + if let Some(dir_path) = file_path.parent() { + if worktree.read_with(&cx, |tree, _| tree.entry_for_path(dir_path).is_none()) { + project + .update(&mut cx, |project, cx| { + project.create_entry((tree_id, dir_path), true, cx) + }) + .ok_or_else(|| anyhow!("worktree was removed"))? + .await?; + } + } + + if worktree.read_with(&cx, |tree, _| tree.entry_for_path(file_path).is_none()) { + project + .update(&mut cx, |project, cx| { + project.create_entry((tree_id, file_path), false, cx) + }) + .ok_or_else(|| anyhow!("worktree was removed"))? + .await?; + } + + workspace + .update(&mut cx, |workspace, cx| { + workspace.open_path((tree_id, file_path), None, true, cx) + })? + .await?; + + anyhow::Ok(()) + }) + .detach(); + } +} + fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { workspace.with_local_workspace(cx, move |workspace, cx| { let app_state = workspace.app_state().clone(); From 296a0bf51060eab6c8eaf1b530f529ee62bce04a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Jun 2023 18:09:42 -0700 Subject: [PATCH 081/131] Populate created local settings file with an empty JSON object and comments --- assets/settings/initial_local_settings.json | 11 +++++++++ crates/collab/src/rpc.rs | 4 +-- crates/settings/src/settings.rs | 19 ++++++++++++--- crates/settings/src/settings_file.rs | 14 +++-------- crates/zed/src/zed.rs | 27 ++++++++++++++++++--- 5 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 assets/settings/initial_local_settings.json diff --git a/assets/settings/initial_local_settings.json b/assets/settings/initial_local_settings.json new file mode 100644 index 0000000000..69be683aa8 --- /dev/null +++ b/assets/settings/initial_local_settings.json @@ -0,0 +1,11 @@ +// Folder-specific Zed settings +// +// A subset of Zed's settings can be configured on a per-folder basis. +// +// For information on how to configure Zed, see the Zed +// documentation: https://zed.dev/docs/configuring-zed +// +// To see all of Zed's default settings without changing your +// custom settings, run the `open default settings` command +// from the command palette or from `Zed` application menu. +{} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 61c6123a82..8d210513c2 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1424,7 +1424,7 @@ async fn join_project( )?; } - for settings_file in dbg!(worktree.settings_files) { + for settings_file in worktree.settings_files { session.peer.send( session.connection_id, proto::UpdateWorktreeSettings { @@ -1554,8 +1554,6 @@ async fn update_worktree_settings( message: proto::UpdateWorktreeSettings, session: Session, ) -> Result<()> { - dbg!(&message); - let guest_connection_ids = session .db() .await diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 840797c6ad..1c131e5916 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -9,10 +9,23 @@ pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; use std::{borrow::Cow, str}; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; -pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; +const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; +const INITIAL_LOCAL_SETTINGS_ASSET_PATH: &str = "settings/initial_local_settings.json"; -pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> { - match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() { +pub fn default_settings() -> Cow<'static, str> { + asset_str(&assets::Assets, DEFAULT_SETTINGS_ASSET_PATH) +} + +pub fn initial_user_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { + asset_str(assets, INITIAL_USER_SETTINGS_ASSET_PATH) +} + +pub fn initial_local_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { + asset_str(assets, INITIAL_LOCAL_SETTINGS_ASSET_PATH) +} + +fn asset_str<'a>(assets: &'a dyn AssetSource, path: &str) -> Cow<'a, str> { + match assets.load(path).unwrap() { Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 3505330eda..edd4fe0d9d 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,11 +1,10 @@ -use crate::{settings_store::SettingsStore, Setting, DEFAULT_SETTINGS_ASSET_PATH}; +use crate::{settings_store::SettingsStore, Setting}; use anyhow::Result; use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; -use gpui::{executor::Background, AppContext, AssetSource}; +use gpui::{executor::Background, AppContext}; use std::{ - borrow::Cow, io::ErrorKind, path::{Path, PathBuf}, str, @@ -28,19 +27,12 @@ pub fn get_local<'a, T: Setting>(location: Option<(usize, &Path)>, cx: &'a AppCo cx.global::().get(location) } -pub fn default_settings() -> Cow<'static, str> { - match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() { - Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), - Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), - } -} - pub const EMPTY_THEME_NAME: &'static str = "empty-theme"; #[cfg(any(test, feature = "test-support"))] pub fn test_settings() -> String { let mut value = crate::settings_store::parse_json_with_comments::( - default_settings().as_ref(), + crate::default_settings().as_ref(), ) .unwrap(); util::merge_non_null_json_value_into( diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index ae9370384a..f0e88133f3 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -30,7 +30,9 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH}; +use settings::{ + initial_local_settings_content, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, +}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_panel::{self, TerminalPanel}; use util::{ @@ -596,11 +598,30 @@ fn open_local_settings_file( .await?; } - workspace + let editor = workspace .update(&mut cx, |workspace, cx| { workspace.open_path((tree_id, file_path), None, true, cx) })? - .await?; + .await? + .downcast::() + .ok_or_else(|| anyhow!("unexpected item type"))?; + + editor + .downgrade() + .update(&mut cx, |editor, cx| { + if let Some(buffer) = editor.buffer().read(cx).as_singleton() { + if buffer.read(cx).is_empty() { + buffer.update(cx, |buffer, cx| { + buffer.edit( + [(0..0, initial_local_settings_content(&Assets))], + None, + cx, + ) + }); + } + } + }) + .ok(); anyhow::Ok(()) }) From 23836eb25119538283a38d697d7606a1fbcf7409 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 5 Jun 2023 22:58:08 -0600 Subject: [PATCH 082/131] Not working yet: Remove empty messages unless they contain the cursor Problem is, I'm trying to trust the excerpt id of the selection head, but it's a sentinel value and not the actual excerpt id of the message. I think we probably need to resolve to offsets instead. --- crates/ai/src/assistant.rs | 93 +++++++++++++++++++++- crates/editor/src/selections_collection.rs | 3 + 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 7020f9f38c..dcb658a0ad 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -4,7 +4,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; -use collections::HashMap; +use collections::{HashMap, HashSet}; use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer}; use fs::Fs; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; @@ -545,6 +545,59 @@ impl Assistant { self.pending_completions.pop().is_some() } + fn remove_empty_messages<'a>( + &mut self, + protected: impl IntoIterator, + cx: &mut ModelContext, + ) { + dbg!(&self.messages_by_id); + + let protected = protected + .into_iter() + .filter_map(|excerpt_id| { + self.messages_by_id + .get(&excerpt_id) + .map(|message| message.content.id()) + }) + .collect::>(); + + dbg!(&protected); + + let empty = self + .messages_by_id + .values() + .filter_map(|message| { + if message.content.read(cx).is_empty() { + Some(message.content.id()) + } else { + None + } + }) + .collect::>(); + + dbg!(&empty); + + let mut remove_excerpts = Vec::new(); + self.messages.retain(|message| { + let is_empty = empty.contains(&message.content.id()); + let is_protected = dbg!(&protected).contains(&message.content.id()); + let retain_message = !is_empty || is_protected; + if !retain_message { + remove_excerpts.push(message.excerpt_id); + self.messages_by_id.remove(&message.excerpt_id); + } + retain_message + }); + + if !remove_excerpts.is_empty() { + self.buffer.update(cx, |buffer, cx| { + dbg!(buffer.excerpt_ids()); + buffer.remove_excerpts(dbg!(remove_excerpts), cx) + }); + cx.notify(); + } + } + fn push_message(&mut self, role: Role, cx: &mut ModelContext) -> Message { let content = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx); @@ -578,6 +631,7 @@ impl Assistant { }); let message = Message { + excerpt_id, role, content: content.clone(), sent_at: Local::now(), @@ -657,8 +711,14 @@ impl AssistantEditor { ); editor }); + + let _subscriptions = vec![ + cx.observe(&assistant, |_, _, cx| cx.notify()), + cx.subscribe(&editor, Self::handle_editor_event), + ]; + Self { - _subscriptions: vec![cx.observe(&assistant, |_, _, cx| cx.notify())], + _subscriptions, assistant, editor, } @@ -678,6 +738,32 @@ impl AssistantEditor { } } + fn handle_editor_event( + &mut self, + _: ViewHandle, + event: &editor::Event, + cx: &mut ViewContext, + ) { + if *event == editor::Event::Edited { + self.remove_empty_messages(cx); + } + } + + // Removes empty messages that don't contain a cursor. + fn remove_empty_messages(&mut self, cx: &mut ViewContext) { + let anchored_selections = self.editor.read(cx).selections.disjoint_anchors(); + let protected_excerpts = anchored_selections + .iter() + .map(|selection| selection.head().excerpt_id()) + .collect::>(); + + dbg!(&protected_excerpts); + + self.assistant.update(cx, |assistant, cx| { + assistant.remove_empty_messages(protected_excerpts, cx) + }); + } + fn quote_selection( workspace: &mut Workspace, _: &QuoteSelection, @@ -804,8 +890,9 @@ impl Item for AssistantEditor { } } -#[derive(Clone)] +#[derive(Clone, Debug)] struct Message { + excerpt_id: ExcerptId, role: Role, content: ModelHandle, sent_at: DateTime, diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 4c32b543b2..d82ce5e216 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -76,6 +76,9 @@ impl SelectionsCollection { count } + /// The non-pending, non-overlapping selections. There could still be a pending + /// selection that overlaps these if the mouse is being dragged, etc. Returned as + /// selections over Anchors. pub fn disjoint_anchors(&self) -> Arc<[Selection]> { self.disjoint.clone() } From e46d1549d6869bdce7476ee360ae644e91b1298f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 10:12:15 +0200 Subject: [PATCH 083/131] Retain selection's head (as opposed to its end) on insertion This makes a difference when an edit spans two excerpts and the selection start won't necessarily be the same as the selection end after the edit. --- crates/editor/src/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 453468349b..0eefa7e098 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2382,7 +2382,7 @@ impl Editor { old_selections .iter() .map(|s| { - let anchor = snapshot.anchor_after(s.end); + let anchor = snapshot.anchor_after(s.head()); s.map(|_| anchor) }) .collect::>() From 80323244707e3d934cd4d04f9074ee9618e0cd28 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 10:13:45 +0200 Subject: [PATCH 084/131] Prevent moving across excerpts on `Editor::delete` --- crates/editor/src/editor.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0eefa7e098..9f2c0c86ff 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3557,7 +3557,9 @@ impl Editor { s.move_with(|map, selection| { if selection.is_empty() && !line_mode { let cursor = movement::right(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); + selection.end = cursor; + selection.reversed = true; + selection.goal = SelectionGoal::None; } }) }); From 337dda8e3a8fd21aed3c6a9a482493f37bc063d9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 12:25:09 +0200 Subject: [PATCH 085/131] Only remove excerpts when an edit touches them --- crates/ai/src/assistant.rs | 100 ++++++++++++------------------ crates/editor/src/editor.rs | 2 +- crates/editor/src/multi_buffer.rs | 11 ++++ 3 files changed, 52 insertions(+), 61 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index dcb658a0ad..1141bcee8e 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -392,6 +392,10 @@ impl Panel for AssistantPanel { } } +enum AssistantEvent { + MessagesEdited { ids: Vec }, +} + struct Assistant { buffer: ModelHandle, messages: Vec, @@ -408,7 +412,7 @@ struct Assistant { } impl Entity for Assistant { - type Event = (); + type Event = AssistantEvent; } impl Assistant { @@ -448,6 +452,9 @@ impl Assistant { editor::multi_buffer::Event::ExcerptsAdded { .. } | editor::multi_buffer::Event::ExcerptsRemoved { .. } | editor::multi_buffer::Event::Edited => self.count_remaining_tokens(cx), + editor::multi_buffer::Event::ExcerptsEdited { ids } => { + cx.emit(AssistantEvent::MessagesEdited { ids: ids.clone() }); + } _ => {} } } @@ -547,52 +554,30 @@ impl Assistant { fn remove_empty_messages<'a>( &mut self, - protected: impl IntoIterator, + excerpts: HashSet, + protected_offsets: HashSet, cx: &mut ModelContext, ) { - dbg!(&self.messages_by_id); - - let protected = protected - .into_iter() - .filter_map(|excerpt_id| { - self.messages_by_id - .get(&excerpt_id) - .map(|message| message.content.id()) - }) - .collect::>(); - - dbg!(&protected); - - let empty = self - .messages_by_id - .values() - .filter_map(|message| { - if message.content.read(cx).is_empty() { - Some(message.content.id()) - } else { - None - } - }) - .collect::>(); - - dbg!(&empty); - - let mut remove_excerpts = Vec::new(); + let mut offset = 0; + let mut excerpts_to_remove = Vec::new(); self.messages.retain(|message| { - let is_empty = empty.contains(&message.content.id()); - let is_protected = dbg!(&protected).contains(&message.content.id()); - let retain_message = !is_empty || is_protected; - if !retain_message { - remove_excerpts.push(message.excerpt_id); + let range = offset..offset + message.content.read(cx).len(); + offset = range.end + 1; + if range.is_empty() + && !protected_offsets.contains(&range.start) + && excerpts.contains(&message.excerpt_id) + { + excerpts_to_remove.push(message.excerpt_id); self.messages_by_id.remove(&message.excerpt_id); + false + } else { + true } - retain_message }); - if !remove_excerpts.is_empty() { + if !excerpts_to_remove.is_empty() { self.buffer.update(cx, |buffer, cx| { - dbg!(buffer.excerpt_ids()); - buffer.remove_excerpts(dbg!(remove_excerpts), cx) + buffer.remove_excerpts(excerpts_to_remove, cx) }); cx.notify(); } @@ -714,7 +699,7 @@ impl AssistantEditor { let _subscriptions = vec![ cx.observe(&assistant, |_, _, cx| cx.notify()), - cx.subscribe(&editor, Self::handle_editor_event), + cx.subscribe(&assistant, Self::handle_assistant_event), ]; Self { @@ -738,32 +723,27 @@ impl AssistantEditor { } } - fn handle_editor_event( + fn handle_assistant_event( &mut self, - _: ViewHandle, - event: &editor::Event, + assistant: ModelHandle, + event: &AssistantEvent, cx: &mut ViewContext, ) { - if *event == editor::Event::Edited { - self.remove_empty_messages(cx); + match event { + AssistantEvent::MessagesEdited { ids } => { + let selections = self.editor.read(cx).selections.all::(cx); + let selection_heads = selections + .iter() + .map(|selection| selection.head()) + .collect::>(); + let ids = ids.iter().copied().collect::>(); + assistant.update(cx, |assistant, cx| { + assistant.remove_empty_messages(ids, selection_heads, cx) + }); + } } } - // Removes empty messages that don't contain a cursor. - fn remove_empty_messages(&mut self, cx: &mut ViewContext) { - let anchored_selections = self.editor.read(cx).selections.disjoint_anchors(); - let protected_excerpts = anchored_selections - .iter() - .map(|selection| selection.head().excerpt_id()) - .collect::>(); - - dbg!(&protected_excerpts); - - self.assistant.update(cx, |assistant, cx| { - assistant.remove_empty_messages(protected_excerpts, cx) - }); - } - fn quote_selection( workspace: &mut Workspace, _: &QuoteSelection, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9f2c0c86ff..613536a955 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6907,7 +6907,7 @@ impl Editor { multi_buffer::Event::DiagnosticsUpdated => { self.refresh_active_diagnostics(cx); } - multi_buffer::Event::LanguageChanged => {} + _ => {} } } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index b7b2b89c8c..6011d10142 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -64,6 +64,9 @@ pub enum Event { ExcerptsRemoved { ids: Vec, }, + ExcerptsEdited { + ids: Vec, + }, Edited, Reloaded, DiffBaseChanged, @@ -387,6 +390,7 @@ impl MultiBuffer { original_indent_column: u32, } let mut buffer_edits: HashMap> = Default::default(); + let mut edited_excerpt_ids = Vec::new(); let mut cursor = snapshot.excerpts.cursor::(); for (ix, (range, new_text)) in edits.enumerate() { let new_text: Arc = new_text.into(); @@ -403,6 +407,7 @@ impl MultiBuffer { .start .to_offset(&start_excerpt.buffer) + start_overshoot; + edited_excerpt_ids.push(start_excerpt.id); cursor.seek(&range.end, Bias::Right, &()); if cursor.item().is_none() && range.end == *cursor.start() { @@ -428,6 +433,7 @@ impl MultiBuffer { original_indent_column, }); } else { + edited_excerpt_ids.push(end_excerpt.id); let start_excerpt_range = buffer_start ..start_excerpt .range @@ -474,6 +480,7 @@ impl MultiBuffer { is_insertion: false, original_indent_column, }); + edited_excerpt_ids.push(excerpt.id); cursor.next(&()); } } @@ -546,6 +553,10 @@ impl MultiBuffer { buffer.edit(insertions, insertion_autoindent_mode, cx); }) } + + cx.emit(Event::ExcerptsEdited { + ids: edited_excerpt_ids, + }); } pub fn start_transaction(&mut self, cx: &mut ModelContext) -> Option { From f4f060667e588536510679b78c10882371ff0e11 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 12:36:26 +0200 Subject: [PATCH 086/131] Add assertion to pinpoint how deletion works across excerpts --- crates/editor/src/editor_tests.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index bc671b9ffc..df6459918e 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5225,7 +5225,28 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { Point::new(0, 1)..Point::new(0, 1), Point::new(1, 1)..Point::new(1, 1), ] - ) + ); + + // Ensure the cursor's head is respected when deleting across an excerpt boundary. + view.change_selections(None, cx, |s| { + s.select_ranges([Point::new(0, 2)..Point::new(1, 2)]) + }); + view.backspace(&Default::default(), cx); + assert_eq!(view.text(cx), "Xa\nbbb"); + assert_eq!( + view.selections.ranges(cx), + [Point::new(1, 0)..Point::new(1, 0)] + ); + + view.change_selections(None, cx, |s| { + s.select_ranges([Point::new(1, 1)..Point::new(0, 1)]) + }); + view.backspace(&Default::default(), cx); + assert_eq!(view.text(cx), "X\nbb"); + assert_eq!( + view.selections.ranges(cx), + [Point::new(0, 1)..Point::new(0, 1)] + ); }); } From ada222078c7d83fe479fa0c51a9fdf749dbff17b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 12:43:08 +0200 Subject: [PATCH 087/131] Insert a user reply when hitting `cmd-enter` in an assistant message --- crates/ai/src/assistant.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1141bcee8e..6c3189b660 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -5,7 +5,7 @@ use crate::{ use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; use collections::{HashMap, HashSet}; -use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer}; +use editor::{Anchor, Editor, ExcerptId, ExcerptRange, MultiBuffer}; use fs::Fs; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ @@ -710,8 +710,25 @@ impl AssistantEditor { } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { - self.assistant - .update(cx, |assistant, cx| assistant.assist(cx)); + self.assistant.update(cx, |assistant, cx| { + let editor = self.editor.read(cx); + let newest_selection = editor.selections.newest_anchor(); + let message = if newest_selection.head() == Anchor::min() { + assistant.messages.first() + } else if newest_selection.head() == Anchor::max() { + assistant.messages.last() + } else { + assistant + .messages_by_id + .get(&newest_selection.head().excerpt_id()) + }; + + if message.map_or(false, |message| message.role == Role::Assistant) { + assistant.push_message(Role::User, cx); + } else { + assistant.assist(cx); + } + }); } fn cancel_last_assist(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { From 69b8267b6bd6eec3b65e1f2b32e237bf0f828e3a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 13:04:52 +0200 Subject: [PATCH 088/131] Show the current model and allow clicking on it to change it --- crates/ai/src/assistant.rs | 85 +++++++++++++++++++++---------- crates/theme/src/theme.rs | 2 + styles/src/styleTree/assistant.ts | 26 ++++++++-- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 6c3189b660..d61dec9f72 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -9,15 +9,17 @@ use editor::{Anchor, Editor, ExcerptId, ExcerptRange, MultiBuffer}; use fs::Fs; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ - actions, elements::*, executor::Background, Action, AppContext, AsyncAppContext, Entity, - ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, - WindowContext, + actions, + elements::*, + executor::Background, + platform::{CursorStyle, MouseButton}, + Action, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Subscription, Task, + View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use settings::SettingsStore; use std::{cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; -use tiktoken_rs::model::get_context_size; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, @@ -430,7 +432,7 @@ impl Assistant { pending_completions: Default::default(), languages: language_registry, token_count: None, - max_token_count: get_context_size(model), + max_token_count: tiktoken_rs::model::get_context_size(model), pending_token_count: Task::ready(None), model: model.into(), _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], @@ -483,6 +485,7 @@ impl Assistant { .await?; this.update(&mut cx, |this, cx| { + this.max_token_count = tiktoken_rs::model::get_context_size(&this.model); this.token_count = Some(token_count); cx.notify() }); @@ -496,6 +499,12 @@ impl Assistant { Some(self.max_token_count as isize - self.token_count? as isize) } + fn set_model(&mut self, model: String, cx: &mut ModelContext) { + self.model = model; + self.count_remaining_tokens(cx); + cx.notify(); + } + fn assist(&mut self, cx: &mut ModelContext) { let messages = self .messages @@ -825,6 +834,16 @@ impl AssistantEditor { }); } } + + fn cycle_model(&mut self, cx: &mut ViewContext) { + self.assistant.update(cx, |assistant, cx| { + let new_model = match assistant.model.as_str() { + "gpt-4" => "gpt-3.5-turbo", + _ => "gpt-4", + }; + assistant.set_model(new_model.into(), cx); + }); + } } impl Entity for AssistantEditor { @@ -837,27 +856,23 @@ impl View for AssistantEditor { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + enum Model {} let theme = &theme::current(cx).assistant; - let remaining_tokens = self - .assistant - .read(cx) - .remaining_tokens() - .map(|remaining_tokens| { - let remaining_tokens_style = if remaining_tokens <= 0 { - &theme.no_remaining_tokens - } else { - &theme.remaining_tokens - }; - Label::new( - remaining_tokens.to_string(), - remaining_tokens_style.text.clone(), - ) - .contained() - .with_style(remaining_tokens_style.container) - .aligned() - .top() - .right() - }); + let assistant = &self.assistant.read(cx); + let model = assistant.model.clone(); + let remaining_tokens = assistant.remaining_tokens().map(|remaining_tokens| { + let remaining_tokens_style = if remaining_tokens <= 0 { + &theme.no_remaining_tokens + } else { + &theme.remaining_tokens + }; + Label::new( + remaining_tokens.to_string(), + remaining_tokens_style.text.clone(), + ) + .contained() + .with_style(remaining_tokens_style.container) + }); Stack::new() .with_child( @@ -865,7 +880,25 @@ impl View for AssistantEditor { .contained() .with_style(theme.container), ) - .with_children(remaining_tokens) + .with_child( + Flex::row() + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.model.style_for(state, false); + Label::new(model, style.text.clone()) + .contained() + .with_style(style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, this, cx| this.cycle_model(cx)), + ) + .with_children(remaining_tokens) + .contained() + .with_style(theme.model_info_container) + .aligned() + .top() + .right(), + ) .into_any() } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 97aac92afd..f746f90193 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -976,6 +976,8 @@ pub struct AssistantStyle { pub sent_at: ContainedText, pub user_sender: ContainedText, pub assistant_sender: ContainedText, + pub model_info_container: ContainerStyle, + pub model: Interactive, pub remaining_tokens: ContainedText, pub no_remaining_tokens: ContainedText, pub api_key_editor: FieldEditor, diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 3d21ee8519..217476bc31 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -11,7 +11,8 @@ export default function assistant(colorScheme: ColorScheme) { }, header: { border: border(layer, "default", { bottom: true, top: true }), - margin: { bottom: 6, top: 6 } + margin: { bottom: 6, top: 6 }, + background: editor(colorScheme).background }, user_sender: { ...text(layer, "sans", "default", { size: "sm", weight: "bold" }), @@ -23,17 +24,32 @@ export default function assistant(colorScheme: ColorScheme) { margin: { top: 2, left: 8 }, ...text(layer, "sans", "default", { size: "2xs" }), }, - remaining_tokens: { - padding: 4, + model_info_container: { margin: { right: 16, top: 4 }, + }, + model: { background: background(layer, "on"), + border: border(layer, "on", { overlay: true }), + padding: 4, + cornerRadius: 4, + ...text(layer, "sans", "default", { size: "xs" }), + hover: { + background: background(layer, "on", "hovered"), + } + }, + remaining_tokens: { + background: background(layer, "on"), + border: border(layer, "on", { overlay: true }), + padding: 4, + margin: { left: 4 }, cornerRadius: 4, ...text(layer, "sans", "positive", { size: "xs" }), }, no_remaining_tokens: { - padding: 4, - margin: { right: 16, top: 4 }, background: background(layer, "on"), + border: border(layer, "on", { overlay: true }), + padding: 4, + margin: { left: 4 }, cornerRadius: 4, ...text(layer, "sans", "negative", { size: "xs" }), }, From 9c59146026eedc3fa5fa5fa2d4121f47eaec9eb5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 15:59:10 +0200 Subject: [PATCH 089/131] Set assistant editor's title based on the first question/answer pair Co-Authored-By: Julia Risley --- assets/keymaps/default.json | 2 +- crates/ai/src/assistant.rs | 119 +++++++++++++++++++++++++++++++---- crates/workspace/src/pane.rs | 3 +- 3 files changed, 111 insertions(+), 13 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 41087326d2..71c34da201 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -197,7 +197,7 @@ } }, { - "context": "ContextEditor > Editor", + "context": "AssistantEditor > Editor", "bindings": { "cmd-enter": "assistant::Assist", "cmd->": "assistant::QuoteSelection" diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index d61dec9f72..0f7ca509d4 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -19,8 +19,8 @@ use gpui::{ use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use settings::SettingsStore; -use std::{cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; -use util::{post_inc, ResultExt, TryFutureExt}; +use std::{borrow::Cow, cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; +use util::{post_inc, truncate_and_trailoff, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, item::Item, @@ -69,7 +69,7 @@ pub struct AssistantPanel { has_read_credentials: bool, languages: Arc, fs: Arc, - _subscriptions: Vec, + subscriptions: Vec, } impl AssistantPanel { @@ -145,11 +145,11 @@ impl AssistantPanel { fs: workspace.app_state().fs.clone(), width: None, height: None, - _subscriptions: Default::default(), + subscriptions: Default::default(), }; let mut old_dock_position = this.position(cx); - this._subscriptions = vec![ + this.subscriptions = vec![ cx.observe(&this.pane, |_, _, cx| cx.notify()), cx.subscribe(&this.pane, Self::handle_pane_event), cx.observe_global::(move |this, cx| { @@ -186,11 +186,24 @@ impl AssistantPanel { let focus = self.has_focus(cx); let editor = cx .add_view(|cx| AssistantEditor::new(self.api_key.clone(), self.languages.clone(), cx)); + self.subscriptions + .push(cx.subscribe(&editor, Self::handle_assistant_editor_event)); self.pane.update(cx, |pane, cx| { pane.add_item(Box::new(editor), true, focus, None, cx) }); } + fn handle_assistant_editor_event( + &mut self, + _: ViewHandle, + event: &AssistantEditorEvent, + cx: &mut ViewContext, + ) { + match event { + AssistantEditorEvent::TabContentChanged => self.pane.update(cx, |_, cx| cx.notify()), + } + } + fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { if let Some(api_key) = self .api_key_editor @@ -396,12 +409,15 @@ impl Panel for AssistantPanel { enum AssistantEvent { MessagesEdited { ids: Vec }, + SummaryChanged, } struct Assistant { buffer: ModelHandle, messages: Vec, messages_by_id: HashMap, + summary: Option, + pending_summary: Task>, completion_count: usize, pending_completions: Vec, languages: Arc, @@ -428,6 +444,8 @@ impl Assistant { let mut this = Self { messages: Default::default(), messages_by_id: Default::default(), + summary: None, + pending_summary: Task::ready(None), completion_count: Default::default(), pending_completions: Default::default(), languages: language_registry, @@ -540,9 +558,10 @@ impl Assistant { } } - this.update(&mut cx, |this, _| { + this.update(&mut cx, |this, cx| { this.pending_completions .retain(|completion| completion.id != this.completion_count); + this.summarize(cx); }); anyhow::Ok(()) @@ -634,6 +653,54 @@ impl Assistant { self.messages_by_id.insert(excerpt_id, message.clone()); message } + + fn summarize(&mut self, cx: &mut ModelContext) { + if self.messages.len() >= 2 && self.summary.is_none() { + let api_key = self.api_key.borrow().clone(); + if let Some(api_key) = api_key { + let messages = self + .messages + .iter() + .take(2) + .map(|message| RequestMessage { + role: message.role, + content: message.content.read(cx).text(), + }) + .chain(Some(RequestMessage { + role: Role::User, + content: "Summarize the conversation into a short title without punctuation and with as few characters as possible" + .into(), + })) + .collect(); + let request = OpenAIRequest { + model: self.model.clone(), + messages, + stream: true, + }; + + let stream = stream_completion(api_key, cx.background().clone(), request); + self.pending_summary = cx.spawn(|this, mut cx| { + async move { + let mut messages = stream.await?; + + while let Some(message) = messages.next().await { + let mut message = message?; + if let Some(choice) = message.choices.pop() { + let text = choice.delta.content.unwrap_or_default(); + this.update(&mut cx, |this, cx| { + this.summary.get_or_insert(String::new()).push_str(&text); + cx.emit(AssistantEvent::SummaryChanged); + }); + } + } + + anyhow::Ok(()) + } + .log_err() + }); + } + } + } } struct PendingCompletion { @@ -641,6 +708,10 @@ struct PendingCompletion { _task: Task>, } +enum AssistantEditorEvent { + TabContentChanged, +} + struct AssistantEditor { assistant: ModelHandle, editor: ViewHandle, @@ -712,9 +783,9 @@ impl AssistantEditor { ]; Self { - _subscriptions, assistant, editor, + _subscriptions, } } @@ -767,6 +838,9 @@ impl AssistantEditor { assistant.remove_empty_messages(ids, selection_heads, cx) }); } + AssistantEvent::SummaryChanged => { + cx.emit(AssistantEditorEvent::TabContentChanged); + } } } @@ -844,15 +918,23 @@ impl AssistantEditor { assistant.set_model(new_model.into(), cx); }); } + + fn title(&self, cx: &AppContext) -> String { + self.assistant + .read(cx) + .summary + .clone() + .unwrap_or_else(|| "New Context".into()) + } } impl Entity for AssistantEditor { - type Event = (); + type Event = AssistantEditorEvent; } impl View for AssistantEditor { fn ui_name() -> &'static str { - "ContextEditor" + "AssistantEditor" } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { @@ -914,9 +996,14 @@ impl Item for AssistantEditor { &self, _: Option, style: &theme::Tab, - _: &gpui::AppContext, + cx: &gpui::AppContext, ) -> AnyElement { - Label::new("New Context", style.label.clone()).into_any() + let title = truncate_and_trailoff(&self.title(cx), editor::MAX_TAB_TITLE_LEN); + Label::new(title, style.label.clone()).into_any() + } + + fn tab_tooltip_text(&self, cx: &AppContext) -> Option> { + Some(self.title(cx).into()) } } @@ -964,9 +1051,19 @@ async fn stream_completion( while let Some(line) = lines.next().await { if let Some(event) = parse_line(line).transpose() { + let done = event.as_ref().map_or(false, |event| { + event + .choices + .last() + .map_or(false, |choice| choice.finish_reason.is_some()) + }); if tx.unbounded_send(event).is_err() { break; } + + if done { + break; + } } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 1fa8c15f9b..ca5e4e981c 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1151,7 +1151,8 @@ impl Pane { let theme = theme::current(cx).clone(); let mut tooltip_theme = theme.tooltip.clone(); tooltip_theme.max_text_width = None; - let tab_tooltip_text = item.tab_tooltip_text(cx).map(|a| a.to_string()); + let tab_tooltip_text = + item.tab_tooltip_text(cx).map(|text| text.into_owned()); move |mouse_state, cx| { let tab_style = From 2b1aeb07bc7acd378af090e403431585943139e4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 16:53:56 +0200 Subject: [PATCH 090/131] Show error message when requests to OpenAI fail Co-Authored-By: Julia Risley --- crates/ai/src/assistant.rs | 124 ++++++++++++++++++++++-------- crates/theme/src/theme.rs | 1 + styles/src/styleTree/assistant.ts | 19 +++-- 3 files changed, 106 insertions(+), 38 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 0f7ca509d4..b70ed8c87c 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -18,6 +18,7 @@ use gpui::{ }; use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; +use serde::Deserialize; use settings::SettingsStore; use std::{borrow::Cow, cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; use util::{post_inc, truncate_and_trailoff, ResultExt, TryFutureExt}; @@ -415,7 +416,7 @@ enum AssistantEvent { struct Assistant { buffer: ModelHandle, messages: Vec, - messages_by_id: HashMap, + messages_metadata: HashMap, summary: Option, pending_summary: Task>, completion_count: usize, @@ -443,7 +444,7 @@ impl Assistant { let buffer = cx.add_model(|_| MultiBuffer::new(0)); let mut this = Self { messages: Default::default(), - messages_by_id: Default::default(), + messages_metadata: Default::default(), summary: None, pending_summary: Task::ready(None), completion_count: Default::default(), @@ -541,16 +542,16 @@ impl Assistant { let api_key = self.api_key.borrow().clone(); if let Some(api_key) = api_key { let stream = stream_completion(api_key, cx.background().clone(), request); - let response = self.push_message(Role::Assistant, cx); + let (excerpt_id, content) = self.push_message(Role::Assistant, cx); self.push_message(Role::User, cx); - let task = cx.spawn(|this, mut cx| { - async move { + let task = cx.spawn(|this, mut cx| async move { + let stream_completion = async { let mut messages = stream.await?; while let Some(message) = messages.next().await { let mut message = message?; if let Some(choice) = message.choices.pop() { - response.content.update(&mut cx, |content, cx| { + content.update(&mut cx, |content, cx| { let text: Arc = choice.delta.content?.into(); content.edit([(content.len()..content.len(), text)], None, cx); Some(()) @@ -565,8 +566,16 @@ impl Assistant { }); anyhow::Ok(()) + }; + + if let Err(error) = stream_completion.await { + this.update(&mut cx, |this, cx| { + if let Some(metadata) = this.messages_metadata.get_mut(&excerpt_id) { + metadata.error = Some(error.to_string().trim().into()); + cx.notify(); + } + }) } - .log_err() }); self.pending_completions.push(PendingCompletion { @@ -596,7 +605,7 @@ impl Assistant { && excerpts.contains(&message.excerpt_id) { excerpts_to_remove.push(message.excerpt_id); - self.messages_by_id.remove(&message.excerpt_id); + self.messages_metadata.remove(&message.excerpt_id); false } else { true @@ -611,7 +620,11 @@ impl Assistant { } } - fn push_message(&mut self, role: Role, cx: &mut ModelContext) -> Message { + fn push_message( + &mut self, + role: Role, + cx: &mut ModelContext, + ) -> (ExcerptId, ModelHandle) { let content = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx); let markdown = self.languages.language_for_name("Markdown"); @@ -643,15 +656,20 @@ impl Assistant { .unwrap() }); - let message = Message { + self.messages.push(Message { excerpt_id, role, content: content.clone(), - sent_at: Local::now(), - }; - self.messages.push(message.clone()); - self.messages_by_id.insert(excerpt_id, message.clone()); - message + }); + self.messages_metadata.insert( + excerpt_id, + MessageMetadata { + role, + sent_at: Local::now(), + error: None, + }, + ); + (excerpt_id, content) } fn summarize(&mut self, cx: &mut ModelContext) { @@ -705,7 +723,7 @@ impl Assistant { struct PendingCompletion { id: usize, - _task: Task>, + _task: Task<()>, } enum AssistantEditorEvent { @@ -733,9 +751,13 @@ impl AssistantEditor { { let assistant = assistant.clone(); move |_editor, params: editor::RenderExcerptHeaderParams, cx| { - let style = &theme::current(cx).assistant; - if let Some(message) = assistant.read(cx).messages_by_id.get(¶ms.id) { - let sender = match message.role { + enum ErrorTooltip {} + + let theme = theme::current(cx); + let style = &theme.assistant; + if let Some(metadata) = assistant.read(cx).messages_metadata.get(¶ms.id) + { + let sender = match metadata.role { Role::User => Label::new("You", style.user_sender.text.clone()) .contained() .with_style(style.user_sender.container), @@ -755,13 +777,29 @@ impl AssistantEditor { .with_child(sender.aligned()) .with_child( Label::new( - message.sent_at.format("%I:%M%P").to_string(), + metadata.sent_at.format("%I:%M%P").to_string(), style.sent_at.text.clone(), ) .contained() .with_style(style.sent_at.container) .aligned(), ) + .with_children(metadata.error.clone().map(|error| { + Svg::new("icons/circle_x_mark_12.svg") + .with_color(style.error_icon.color) + .constrained() + .with_width(style.error_icon.width) + .contained() + .with_style(style.error_icon.container) + .with_tooltip::( + params.id.into(), + error, + None, + theme.tooltip.clone(), + cx, + ) + .aligned() + })) .aligned() .left() .contained() @@ -793,17 +831,18 @@ impl AssistantEditor { self.assistant.update(cx, |assistant, cx| { let editor = self.editor.read(cx); let newest_selection = editor.selections.newest_anchor(); - let message = if newest_selection.head() == Anchor::min() { - assistant.messages.first() + let role = if newest_selection.head() == Anchor::min() { + assistant.messages.first().map(|message| message.role) } else if newest_selection.head() == Anchor::max() { - assistant.messages.last() + assistant.messages.last().map(|message| message.role) } else { assistant - .messages_by_id + .messages_metadata .get(&newest_selection.head().excerpt_id()) + .map(|message| message.role) }; - if message.map_or(false, |message| message.role == Role::Assistant) { + if role.map_or(false, |role| role == Role::Assistant) { assistant.push_message(Role::User, cx); } else { assistant.assist(cx); @@ -1007,12 +1046,18 @@ impl Item for AssistantEditor { } } -#[derive(Clone, Debug)] +#[derive(Debug)] struct Message { excerpt_id: ExcerptId, role: Role, content: ModelHandle, +} + +#[derive(Debug)] +struct MessageMetadata { + role: Role, sent_at: DateTime, + error: Option, } async fn stream_completion( @@ -1076,10 +1121,27 @@ async fn stream_completion( let mut body = String::new(); response.body_mut().read_to_string(&mut body).await?; - Err(anyhow!( - "Failed to connect to OpenAI API: {} {}", - response.status(), - body, - )) + #[derive(Deserialize)] + struct OpenAIResponse { + error: OpenAIError, + } + + #[derive(Deserialize)] + struct OpenAIError { + message: String, + } + + match serde_json::from_str::(&body) { + Ok(response) if !response.error.message.is_empty() => Err(anyhow!( + "Failed to connect to OpenAI API: {}", + response.error.message, + )), + + _ => Err(anyhow!( + "Failed to connect to OpenAI API: {} {}", + response.status(), + body, + )), + } } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index f746f90193..132e37ad1c 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -980,6 +980,7 @@ pub struct AssistantStyle { pub model: Interactive, pub remaining_tokens: ContainedText, pub no_remaining_tokens: ContainedText, + pub error_icon: Icon, pub api_key_editor: FieldEditor, pub api_key_prompt: ContainedText, } diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 217476bc31..4314741fb0 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -1,5 +1,5 @@ import { ColorScheme } from "../themes/common/colorScheme" -import { text, border, background } from "./components" +import { text, border, background, foreground } from "./components" import editor from "./editor" export default function assistant(colorScheme: ColorScheme) { @@ -14,17 +14,17 @@ export default function assistant(colorScheme: ColorScheme) { margin: { bottom: 6, top: 6 }, background: editor(colorScheme).background }, - user_sender: { + userSender: { ...text(layer, "sans", "default", { size: "sm", weight: "bold" }), }, - assistant_sender: { + assistantSender: { ...text(layer, "sans", "accent", { size: "sm", weight: "bold" }), }, - sent_at: { + sentAt: { margin: { top: 2, left: 8 }, ...text(layer, "sans", "default", { size: "2xs" }), }, - model_info_container: { + modelInfoContainer: { margin: { right: 16, top: 4 }, }, model: { @@ -37,7 +37,7 @@ export default function assistant(colorScheme: ColorScheme) { background: background(layer, "on", "hovered"), } }, - remaining_tokens: { + remainingTokens: { background: background(layer, "on"), border: border(layer, "on", { overlay: true }), padding: 4, @@ -45,7 +45,7 @@ export default function assistant(colorScheme: ColorScheme) { cornerRadius: 4, ...text(layer, "sans", "positive", { size: "xs" }), }, - no_remaining_tokens: { + noRemainingTokens: { background: background(layer, "on"), border: border(layer, "on", { overlay: true }), padding: 4, @@ -53,6 +53,11 @@ export default function assistant(colorScheme: ColorScheme) { cornerRadius: 4, ...text(layer, "sans", "negative", { size: "xs" }), }, + errorIcon: { + margin: { left: 8 }, + color: foreground(layer, "negative"), + width: 12, + }, apiKeyEditor: { background: background(layer, "on"), cornerRadius: 6, From a0e2e5db7dca2157d2693d91e98052c1351ca5f9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:55:38 +0200 Subject: [PATCH 091/131] project panel/styles: Align child's chevron with parent path (#2559) Z-1012 Release notes: - Adjust indent of files in subdirectories. --- styles/src/styleTree/projectPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/styleTree/projectPanel.ts b/styles/src/styleTree/projectPanel.ts index 08117bf6b0..666d236da6 100644 --- a/styles/src/styleTree/projectPanel.ts +++ b/styles/src/styleTree/projectPanel.ts @@ -72,7 +72,7 @@ export default function projectPanel(colorScheme: ColorScheme) { }, background: background(layer), padding: { left: 12, right: 12, top: 6, bottom: 6 }, - indentWidth: 8, + indentWidth: 16, entry, draggedEntry: { ...baseEntry, From 7b066df7e6a03c66b2f393d2b630ce939a1d6b00 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 6 Jun 2023 11:31:53 -0400 Subject: [PATCH 092/131] Tighten up spacing in the project panel (#2574) Following https://github.com/zed-industries/zed/pull/2559 the project panel entries become pretty wide again. This PR tries to mitigate that and just make some general improvements to visual density in the project panel. - Reduces padding around items - Removes top margin - Slightly reduces the height of each item - Fixes an issue where ignored files had the wrong color chevron Release Notes: - Improved density of the project panel and tidied up some visual issues. --- styles/src/styleTree/projectPanel.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/styles/src/styleTree/projectPanel.ts b/styles/src/styleTree/projectPanel.ts index 666d236da6..d2cc129452 100644 --- a/styles/src/styleTree/projectPanel.ts +++ b/styles/src/styleTree/projectPanel.ts @@ -8,10 +8,10 @@ export default function projectPanel(colorScheme: ColorScheme) { let layer = colorScheme.middle let baseEntry = { - height: 24, + height: 22, iconColor: foreground(layer, "variant"), - iconSize: 8, - iconSpacing: 8, + iconSize: 7, + iconSpacing: 5, } let status = { @@ -71,8 +71,8 @@ export default function projectPanel(colorScheme: ColorScheme) { }, }, background: background(layer), - padding: { left: 12, right: 12, top: 6, bottom: 6 }, - indentWidth: 16, + padding: { left: 6, right: 6, top: 0, bottom: 6 }, + indentWidth: 12, entry, draggedEntry: { ...baseEntry, @@ -83,7 +83,12 @@ export default function projectPanel(colorScheme: ColorScheme) { }, ignoredEntry: { ...entry, + iconColor: foreground(layer, "disabled"), text: text(layer, "mono", "disabled"), + active: { + ...entry.active, + iconColor: foreground(layer, "variant"), + } }, cutEntry: { ...entry, From 093ce8a9ac6e20ebfdde2786733f3eff8d571fad Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 17:45:58 +0200 Subject: [PATCH 093/131] Simplify prompt Co-Authored-By: Nathan Sobo --- crates/ai/src/assistant.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index b70ed8c87c..f505ea1f3f 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -686,8 +686,9 @@ impl Assistant { }) .chain(Some(RequestMessage { role: Role::User, - content: "Summarize the conversation into a short title without punctuation and with as few characters as possible" - .into(), + content: + "Summarize the conversation into a short title without punctuation" + .into(), })) .collect(); let request = OpenAIRequest { From cfcfc3bf6b1493e7668ed6a0c433f198690d6358 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 09:03:57 -0700 Subject: [PATCH 094/131] Show notification when attempting to open local settings in a project w/ no folders --- crates/zed/src/zed.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f0e88133f3..8d27a378c5 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -44,8 +44,9 @@ use uuid::Uuid; use welcome::BaseKeymap; pub use workspace; use workspace::{ - create_and_open_local_file, dock::PanelHandle, open_new, AppState, NewFile, NewWindow, - Workspace, WorkspaceSettings, + create_and_open_local_file, dock::PanelHandle, + notifications::simple_message_notification::MessageNotification, open_new, AppState, NewFile, + NewWindow, Workspace, WorkspaceSettings, }; #[derive(Deserialize, Clone, PartialEq)] @@ -626,6 +627,10 @@ fn open_local_settings_file( anyhow::Ok(()) }) .detach(); + } else { + workspace.show_notification(0, cx, |cx| { + cx.add_view(|_| MessageNotification::new("This project has no folders open.")) + }) } } From ac7178068fd06bf000525e9d3071e59c36d7de91 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 18:18:04 +0200 Subject: [PATCH 095/131] Include message headers in copied assistant text Co-Authored-By: Nathan Sobo --- crates/ai/src/ai.rs | 11 +++++++++ crates/ai/src/assistant.rs | 46 +++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 11704de03e..6f26f00c52 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -4,6 +4,7 @@ mod assistant_settings; pub use assistant::AssistantPanel; use gpui::AppContext; use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; // Data types for chat completion requests #[derive(Serialize)] @@ -33,6 +34,16 @@ enum Role { System, } +impl Display for Role { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Role::User => write!(f, "User"), + Role::Assistant => write!(f, "Assistant"), + Role::System => write!(f, "System"), + } + } +} + #[derive(Deserialize, Debug)] struct OpenAIResponseStreamEvent { pub id: Option, diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index f505ea1f3f..a61ecf202d 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -13,14 +13,14 @@ use gpui::{ elements::*, executor::Background, platform::{CursorStyle, MouseButton}, - Action, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Subscription, Task, - View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + Action, AppContext, AsyncAppContext, ClipboardItem, Entity, ModelContext, ModelHandle, + Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use isahc::{http::StatusCode, Request, RequestExt}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use serde::Deserialize; use settings::SettingsStore; -use std::{borrow::Cow, cell::RefCell, io, rc::Rc, sync::Arc, time::Duration}; +use std::{borrow::Cow, cell::RefCell, cmp, fmt::Write, io, rc::Rc, sync::Arc, time::Duration}; use util::{post_inc, truncate_and_trailoff, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, @@ -49,6 +49,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(AssistantEditor::assist); cx.capture_action(AssistantEditor::cancel_last_assist); cx.add_action(AssistantEditor::quote_selection); + cx.capture_action(AssistantEditor::copy); cx.add_action(AssistantPanel::save_api_key); cx.add_action(AssistantPanel::reset_api_key); } @@ -949,6 +950,45 @@ impl AssistantEditor { } } + fn copy(&mut self, _: &editor::Copy, cx: &mut ViewContext) { + let editor = self.editor.read(cx); + let assistant = self.assistant.read(cx); + if editor.selections.count() == 1 { + let selection = editor.selections.newest::(cx); + let mut offset = 0; + let mut copied_text = String::new(); + let mut spanned_messages = 0; + for message in &assistant.messages { + let message_range = offset..offset + message.content.read(cx).len() + 1; + + if message_range.start >= selection.range().end { + break; + } else if message_range.end >= selection.range().start { + let range = cmp::max(message_range.start, selection.range().start) + ..cmp::min(message_range.end, selection.range().end); + if !range.is_empty() { + spanned_messages += 1; + write!(&mut copied_text, "## {}\n\n", message.role).unwrap(); + for chunk in assistant.buffer.read(cx).snapshot(cx).text_for_range(range) { + copied_text.push_str(&chunk); + } + copied_text.push('\n'); + } + } + + offset = message_range.end; + } + + if spanned_messages > 1 { + cx.platform() + .write_to_clipboard(ClipboardItem::new(copied_text)); + return; + } + } + + cx.propagate_action(); + } + fn cycle_model(&mut self, cx: &mut ViewContext) { self.assistant.update(cx, |assistant, cx| { let new_model = match assistant.model.as_str() { From ef7ec265c8c288ab3c0d83c0c31d84815b17365d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 18:45:08 +0200 Subject: [PATCH 096/131] Cycle message roles on click Co-Authored-By: Nathan Sobo --- crates/ai/src/ai.rs | 10 +++ crates/ai/src/assistant.rs | 139 +++++++++++++++++++----------- crates/theme/src/theme.rs | 5 +- styles/src/styleTree/assistant.ts | 3 + 4 files changed, 106 insertions(+), 51 deletions(-) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 6f26f00c52..40224b3229 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -34,6 +34,16 @@ enum Role { System, } +impl Role { + pub fn cycle(&mut self) { + *self = match self { + Role::User => Role::Assistant, + Role::Assistant => Role::System, + Role::System => Role::User, + } + } +} + impl Display for Role { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index a61ecf202d..4a8319015f 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -485,14 +485,16 @@ impl Assistant { let messages = self .messages .iter() - .map(|message| tiktoken_rs::ChatCompletionRequestMessage { - role: match message.role { - Role::User => "user".into(), - Role::Assistant => "assistant".into(), - Role::System => "system".into(), - }, - content: message.content.read(cx).text(), - name: None, + .filter_map(|message| { + Some(tiktoken_rs::ChatCompletionRequestMessage { + role: match self.messages_metadata.get(&message.excerpt_id)?.role { + Role::User => "user".into(), + Role::Assistant => "assistant".into(), + Role::System => "system".into(), + }, + content: message.content.read(cx).text(), + name: None, + }) }) .collect::>(); let model = self.model.clone(); @@ -529,9 +531,11 @@ impl Assistant { let messages = self .messages .iter() - .map(|message| RequestMessage { - role: message.role, - content: message.content.read(cx).text(), + .filter_map(|message| { + Some(RequestMessage { + role: self.messages_metadata.get(&message.excerpt_id)?.role, + content: message.content.read(cx).text(), + }) }) .collect(); let request = OpenAIRequest { @@ -621,6 +625,13 @@ impl Assistant { } } + fn cycle_message_role(&mut self, excerpt_id: ExcerptId, cx: &mut ModelContext) { + if let Some(metadata) = self.messages_metadata.get_mut(&excerpt_id) { + metadata.role.cycle(); + cx.notify(); + } + } + fn push_message( &mut self, role: Role, @@ -659,7 +670,6 @@ impl Assistant { self.messages.push(Message { excerpt_id, - role, content: content.clone(), }); self.messages_metadata.insert( @@ -681,9 +691,11 @@ impl Assistant { .messages .iter() .take(2) - .map(|message| RequestMessage { - role: message.role, - content: message.content.read(cx).text(), + .filter_map(|message| { + Some(RequestMessage { + role: self.messages_metadata.get(&message.excerpt_id)?.role, + content: message.content.read(cx).text(), + }) }) .chain(Some(RequestMessage { role: Role::User, @@ -753,27 +765,51 @@ impl AssistantEditor { { let assistant = assistant.clone(); move |_editor, params: editor::RenderExcerptHeaderParams, cx| { + enum Sender {} enum ErrorTooltip {} let theme = theme::current(cx); let style = &theme.assistant; - if let Some(metadata) = assistant.read(cx).messages_metadata.get(¶ms.id) + let excerpt_id = params.id; + if let Some(metadata) = assistant + .read(cx) + .messages_metadata + .get(&excerpt_id) + .cloned() { - let sender = match metadata.role { - Role::User => Label::new("You", style.user_sender.text.clone()) - .contained() - .with_style(style.user_sender.container), - Role::Assistant => { - Label::new("Assistant", style.assistant_sender.text.clone()) - .contained() - .with_style(style.assistant_sender.container) + let sender = MouseEventHandler::::new( + params.id.into(), + cx, + |state, _| match metadata.role { + Role::User => { + let style = style.user_sender.style_for(state, false); + Label::new("You", style.text.clone()) + .contained() + .with_style(style.container) + } + Role::Assistant => { + let style = style.assistant_sender.style_for(state, false); + Label::new("Assistant", style.text.clone()) + .contained() + .with_style(style.container) + } + Role::System => { + let style = style.system_sender.style_for(state, false); + Label::new("System", style.text.clone()) + .contained() + .with_style(style.container) + } + }, + ) + .with_cursor_style(CursorStyle::PointingHand) + .on_down(MouseButton::Left, { + let assistant = assistant.clone(); + move |_, _, cx| { + assistant.update(cx, |assistant, cx| { + assistant.cycle_message_role(excerpt_id, cx) + }) } - Role::System => { - Label::new("System", style.assistant_sender.text.clone()) - .contained() - .with_style(style.assistant_sender.container) - } - }; + }); Flex::row() .with_child(sender.aligned()) @@ -786,7 +822,7 @@ impl AssistantEditor { .with_style(style.sent_at.container) .aligned(), ) - .with_children(metadata.error.clone().map(|error| { + .with_children(metadata.error.map(|error| { Svg::new("icons/circle_x_mark_12.svg") .with_color(style.error_icon.color) .constrained() @@ -833,21 +869,22 @@ impl AssistantEditor { self.assistant.update(cx, |assistant, cx| { let editor = self.editor.read(cx); let newest_selection = editor.selections.newest_anchor(); - let role = if newest_selection.head() == Anchor::min() { - assistant.messages.first().map(|message| message.role) + let excerpt_id = if newest_selection.head() == Anchor::min() { + assistant.messages.first().map(|message| message.excerpt_id) } else if newest_selection.head() == Anchor::max() { - assistant.messages.last().map(|message| message.role) + assistant.messages.last().map(|message| message.excerpt_id) } else { - assistant - .messages_metadata - .get(&newest_selection.head().excerpt_id()) - .map(|message| message.role) + Some(newest_selection.head().excerpt_id()) }; - if role.map_or(false, |role| role == Role::Assistant) { - assistant.push_message(Role::User, cx); - } else { - assistant.assist(cx); + if let Some(excerpt_id) = excerpt_id { + if let Some(metadata) = assistant.messages_metadata.get(&excerpt_id) { + if metadata.role == Role::User { + assistant.assist(cx); + } else { + assistant.push_message(Role::User, cx); + } + } } }); } @@ -967,12 +1004,17 @@ impl AssistantEditor { let range = cmp::max(message_range.start, selection.range().start) ..cmp::min(message_range.end, selection.range().end); if !range.is_empty() { - spanned_messages += 1; - write!(&mut copied_text, "## {}\n\n", message.role).unwrap(); - for chunk in assistant.buffer.read(cx).snapshot(cx).text_for_range(range) { - copied_text.push_str(&chunk); + if let Some(metadata) = assistant.messages_metadata.get(&message.excerpt_id) + { + spanned_messages += 1; + write!(&mut copied_text, "## {}\n\n", metadata.role).unwrap(); + for chunk in + assistant.buffer.read(cx).snapshot(cx).text_for_range(range) + { + copied_text.push_str(&chunk); + } + copied_text.push('\n'); } - copied_text.push('\n'); } } @@ -1090,11 +1132,10 @@ impl Item for AssistantEditor { #[derive(Debug)] struct Message { excerpt_id: ExcerptId, - role: Role, content: ModelHandle, } -#[derive(Debug)] +#[derive(Clone, Debug)] struct MessageMetadata { role: Role, sent_at: DateTime, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 132e37ad1c..f7df63ca09 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -974,8 +974,9 @@ pub struct AssistantStyle { pub container: ContainerStyle, pub header: ContainerStyle, pub sent_at: ContainedText, - pub user_sender: ContainedText, - pub assistant_sender: ContainedText, + pub user_sender: Interactive, + pub assistant_sender: Interactive, + pub system_sender: Interactive, pub model_info_container: ContainerStyle, pub model: Interactive, pub remaining_tokens: ContainedText, diff --git a/styles/src/styleTree/assistant.ts b/styles/src/styleTree/assistant.ts index 4314741fb0..5e33967b50 100644 --- a/styles/src/styleTree/assistant.ts +++ b/styles/src/styleTree/assistant.ts @@ -20,6 +20,9 @@ export default function assistant(colorScheme: ColorScheme) { assistantSender: { ...text(layer, "sans", "accent", { size: "sm", weight: "bold" }), }, + systemSender: { + ...text(layer, "sans", "variant", { size: "sm", weight: "bold" }), + }, sentAt: { margin: { top: 2, left: 8 }, ...text(layer, "sans", "default", { size: "2xs" }), From 16090c35ae02d3af6bf226ca4de2fe21804a6de0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Jun 2023 19:15:06 +0200 Subject: [PATCH 097/131] Insert reply after assistant message when hitting `cmd-enter` Co-Authored-By: Nathan Sobo --- crates/ai/src/assistant.rs | 104 ++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 4a8319015f..7f387194b3 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -459,7 +459,7 @@ impl Assistant { api_key, buffer, }; - this.push_message(Role::User, cx); + this.insert_message_after(ExcerptId::max(), Role::User, cx); this.count_remaining_tokens(cx); this } @@ -498,7 +498,7 @@ impl Assistant { }) .collect::>(); let model = self.model.clone(); - self.pending_token_count = cx.spawn(|this, mut cx| { + self.pending_token_count = cx.spawn_weak(|this, mut cx| { async move { cx.background().timer(Duration::from_millis(200)).await; let token_count = cx @@ -506,11 +506,13 @@ impl Assistant { .spawn(async move { tiktoken_rs::num_tokens_from_messages(&model, &messages) }) .await?; - this.update(&mut cx, |this, cx| { - this.max_token_count = tiktoken_rs::model::get_context_size(&this.model); - this.token_count = Some(token_count); - cx.notify() - }); + this.upgrade(&cx) + .ok_or_else(|| anyhow!("assistant was dropped"))? + .update(&mut cx, |this, cx| { + this.max_token_count = tiktoken_rs::model::get_context_size(&this.model); + this.token_count = Some(token_count); + cx.notify() + }); anyhow::Ok(()) } .log_err() @@ -547,9 +549,10 @@ impl Assistant { let api_key = self.api_key.borrow().clone(); if let Some(api_key) = api_key { let stream = stream_completion(api_key, cx.background().clone(), request); - let (excerpt_id, content) = self.push_message(Role::Assistant, cx); - self.push_message(Role::User, cx); - let task = cx.spawn(|this, mut cx| async move { + let (excerpt_id, content) = + self.insert_message_after(ExcerptId::max(), Role::Assistant, cx); + self.insert_message_after(ExcerptId::max(), Role::User, cx); + let task = cx.spawn_weak(|this, mut cx| async move { let stream_completion = async { let mut messages = stream.await?; @@ -564,22 +567,26 @@ impl Assistant { } } - this.update(&mut cx, |this, cx| { - this.pending_completions - .retain(|completion| completion.id != this.completion_count); - this.summarize(cx); - }); + this.upgrade(&cx) + .ok_or_else(|| anyhow!("assistant was dropped"))? + .update(&mut cx, |this, cx| { + this.pending_completions + .retain(|completion| completion.id != this.completion_count); + this.summarize(cx); + }); anyhow::Ok(()) }; if let Err(error) = stream_completion.await { - this.update(&mut cx, |this, cx| { - if let Some(metadata) = this.messages_metadata.get_mut(&excerpt_id) { - metadata.error = Some(error.to_string().trim().into()); - cx.notify(); - } - }) + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + if let Some(metadata) = this.messages_metadata.get_mut(&excerpt_id) { + metadata.error = Some(error.to_string().trim().into()); + cx.notify(); + } + }); + } } }); @@ -632,8 +639,9 @@ impl Assistant { } } - fn push_message( + fn insert_message_after( &mut self, + excerpt_id: ExcerptId, role: Role, cx: &mut ModelContext, ) -> (ExcerptId, ModelHandle) { @@ -654,9 +662,10 @@ impl Assistant { buffer.set_language_registry(self.languages.clone()); buffer }); - let excerpt_id = self.buffer.update(cx, |buffer, cx| { + let new_excerpt_id = self.buffer.update(cx, |buffer, cx| { buffer - .push_excerpts( + .insert_excerpts_after( + excerpt_id, content.clone(), vec![ExcerptRange { context: 0..0, @@ -668,19 +677,27 @@ impl Assistant { .unwrap() }); - self.messages.push(Message { - excerpt_id, - content: content.clone(), - }); + let ix = self + .messages + .iter() + .position(|message| message.excerpt_id == excerpt_id) + .map_or(self.messages.len(), |ix| ix + 1); + self.messages.insert( + ix, + Message { + excerpt_id: new_excerpt_id, + content: content.clone(), + }, + ); self.messages_metadata.insert( - excerpt_id, + new_excerpt_id, MessageMetadata { role, sent_at: Local::now(), error: None, }, ); - (excerpt_id, content) + (new_excerpt_id, content) } fn summarize(&mut self, cx: &mut ModelContext) { @@ -882,7 +899,7 @@ impl AssistantEditor { if metadata.role == Role::User { assistant.assist(cx); } else { - assistant.push_message(Role::User, cx); + assistant.insert_message_after(excerpt_id, Role::User, cx); } } } @@ -1227,3 +1244,28 @@ async fn stream_completion( } } } + +#[cfg(test)] +mod tests { + use super::*; + use gpui::AppContext; + + #[gpui::test] + fn test_inserting_and_removing_messages(cx: &mut AppContext) { + let registry = Arc::new(LanguageRegistry::test()); + + cx.add_model(|cx| { + let mut assistant = Assistant::new(Default::default(), registry, cx); + let (excerpt_1, _) = + assistant.insert_message_after(ExcerptId::max(), Role::Assistant, cx); + let (excerpt_2, _) = assistant.insert_message_after(excerpt_1, Role::User, cx); + let (excerpt_3, _) = assistant.insert_message_after(excerpt_1, Role::User, cx); + assistant.remove_empty_messages( + HashSet::from_iter([excerpt_2, excerpt_3]), + Default::default(), + cx, + ); + assistant + }); + } +} From 2003d3dbe4d5e11384439322592c2158ac51585b Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 6 Jun 2023 14:39:56 -0400 Subject: [PATCH 098/131] Update initial_user_settings.json --- assets/settings/initial_user_settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/settings/initial_user_settings.json b/assets/settings/initial_user_settings.json index dc79fd7911..1599814f96 100644 --- a/assets/settings/initial_user_settings.json +++ b/assets/settings/initial_user_settings.json @@ -1,7 +1,7 @@ -// Zed settings +// Folder-specific settings // -// For information on how to configure Zed, see the Zed -// documentation: https://zed.dev/docs/configuring-zed +// For information on how to configure folder-specific settings, see the documentation: +// https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings // // To see all of Zed's default settings without changing your // custom settings, run the `open default settings` command From dbbd0558c38fa8ef09e995c5ed75b93951cb2cad Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 11:46:46 -0700 Subject: [PATCH 099/131] Eliminate assets crate --- Cargo.lock | 18 +---- Cargo.toml | 2 +- crates/ai/Cargo.toml | 2 +- crates/ai/src/ai.rs | 11 ++- crates/assets/Cargo.toml | 14 ---- crates/assets/build.rs | 29 -------- crates/copilot_button/Cargo.toml | 1 - crates/copilot_button/src/copilot_button.rs | 4 +- crates/settings/Cargo.toml | 2 +- crates/settings/src/keymap_file.rs | 76 ++++++++++----------- crates/settings/src/settings.rs | 38 ++++++----- crates/settings/src/settings_file.rs | 3 +- crates/util/Cargo.toml | 1 + crates/util/src/util.rs | 9 +++ crates/vim/Cargo.toml | 1 - crates/vim/src/test/vim_test_context.rs | 2 +- crates/workspace/Cargo.toml | 1 - crates/zed/Cargo.toml | 1 - crates/zed/build.rs | 30 ++++++++ crates/{assets => zed}/src/assets.rs | 4 ++ crates/zed/src/languages.rs | 6 +- crates/zed/src/languages/json.rs | 4 +- crates/zed/src/main.rs | 4 +- crates/zed/src/zed.rs | 39 ++++------- 24 files changed, 142 insertions(+), 160 deletions(-) delete mode 100644 crates/assets/Cargo.toml delete mode 100644 crates/assets/build.rs rename crates/{assets => zed}/src/assets.rs (84%) diff --git a/Cargo.lock b/Cargo.lock index 7f9a553013..a1ba2a4998 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,12 +100,12 @@ name = "ai" version = "0.1.0" dependencies = [ "anyhow", - "assets", "collections", "editor", "futures 0.3.28", "gpui", "isahc", + "rust-embed", "serde", "serde_json", "util", @@ -210,15 +210,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" -[[package]] -name = "assets" -version = "0.1.0" -dependencies = [ - "anyhow", - "gpui", - "rust-embed", -] - [[package]] name = "async-broadcast" version = "0.4.1" @@ -1402,7 +1393,6 @@ name = "copilot_button" version = "0.1.0" dependencies = [ "anyhow", - "assets", "context_menu", "copilot", "editor", @@ -6114,7 +6104,6 @@ name = "settings" version = "0.1.0" dependencies = [ "anyhow", - "assets", "collections", "fs", "futures 0.3.28", @@ -6123,6 +6112,7 @@ dependencies = [ "lazy_static", "postage", "pretty_assertions", + "rust-embed", "schemars", "serde", "serde_derive", @@ -7749,6 +7739,7 @@ dependencies = [ "lazy_static", "log", "rand 0.8.5", + "rust-embed", "serde", "serde_json", "smol", @@ -7819,7 +7810,6 @@ name = "vim" version = "0.1.0" dependencies = [ "anyhow", - "assets", "async-compat", "async-trait", "collections", @@ -8647,7 +8637,6 @@ name = "workspace" version = "0.1.0" dependencies = [ "anyhow", - "assets", "async-recursion 1.0.4", "bincode", "call", @@ -8747,7 +8736,6 @@ dependencies = [ "activity_indicator", "ai", "anyhow", - "assets", "async-compression", "async-recursion 0.3.2", "async-tar", diff --git a/Cargo.toml b/Cargo.toml index f1362e059d..bc614b6783 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "crates/activity_indicator", "crates/ai", - "crates/assets", "crates/auto_update", "crates/breadcrumbs", "crates/call", @@ -88,6 +87,7 @@ parking_lot = { version = "0.11.1" } postage = { version = "0.5", features = ["futures-traits"] } rand = { version = "0.8.5" } regex = { version = "1.5" } +rust-embed = { version = "6.3", features = ["include-exclude"] } schemars = { version = "0.8" } serde = { version = "1.0", features = ["derive", "rc"] } serde_derive = { version = "1.0", features = ["deserialize_in_place"] } diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index b367a4d43c..b6c7553757 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -9,12 +9,12 @@ path = "src/ai.rs" doctest = false [dependencies] -assets = { path = "../assets"} collections = { path = "../collections"} editor = { path = "../editor" } gpui = { path = "../gpui" } util = { path = "../util" } +rust-embed.workspace = true serde.workspace = true serde_json.workspace = true anyhow.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 2a0110510f..15d9bf053e 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,5 +1,4 @@ use anyhow::{anyhow, Result}; -use assets::Assets; use collections::HashMap; use editor::Editor; use futures::AsyncBufReadExt; @@ -16,6 +15,14 @@ use std::{io, sync::Arc}; use util::channel::{ReleaseChannel, RELEASE_CHANNEL}; use util::{ResultExt, TryFutureExt}; +use rust_embed::RustEmbed; +use std::str; + +#[derive(RustEmbed)] +#[folder = "../../assets/contexts"] +#[exclude = "*.DS_Store"] +pub struct ContextAssets; + actions!(ai, [Assist]); // Data types for chat completion requests @@ -173,7 +180,7 @@ impl Assistant { let assist_task = cx.spawn(|_, mut cx| { async move { // TODO: We should have a get_string method on assets. This is repateated elsewhere. - let content = Assets::get("contexts/system.zmd").unwrap(); + let content = ContextAssets::get("system.zmd").unwrap(); let mut system_message = std::str::from_utf8(content.data.as_ref()) .unwrap() .to_string(); diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml deleted file mode 100644 index eec60ff84c..0000000000 --- a/crates/assets/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "assets" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -path = "src/assets.rs" -doctest = false - -[dependencies] -gpui = { path = "../gpui" } -anyhow.workspace = true -rust-embed = { version = "6.3", features = ["include-exclude"] } diff --git a/crates/assets/build.rs b/crates/assets/build.rs deleted file mode 100644 index 8500b24622..0000000000 --- a/crates/assets/build.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::process::Command; - -fn main() { - let output = Command::new("npm") - .current_dir("../../styles") - .args(["install", "--no-save"]) - .output() - .expect("failed to run npm"); - if !output.status.success() { - panic!( - "failed to install theme dependencies {}", - String::from_utf8_lossy(&output.stderr) - ); - } - - let output = Command::new("npm") - .current_dir("../../styles") - .args(["run", "build"]) - .output() - .expect("failed to run npm"); - if !output.status.success() { - panic!( - "build script failed {}", - String::from_utf8_lossy(&output.stderr) - ); - } - - println!("cargo:rerun-if-changed=../../styles/src"); -} diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_button/Cargo.toml index 50fbaa64ee..c93e1920dc 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_button/Cargo.toml @@ -9,7 +9,6 @@ path = "src/copilot_button.rs" doctest = false [dependencies] -assets = { path = "../assets" } copilot = { path = "../copilot" } editor = { path = "../editor" } fs = { path = "../fs" } diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index e34fddd9b9..2454074d45 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -315,9 +315,7 @@ async fn configure_disabled_globs( let settings_editor = workspace .update(&mut cx, |_, cx| { create_and_open_local_file(&paths::SETTINGS, cx, || { - settings::initial_user_settings_content(&assets::Assets) - .as_ref() - .into() + settings::initial_user_settings_content().as_ref().into() }) })? .await? diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 1ec0ff4a63..f0396266fc 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -12,7 +12,6 @@ doctest = false test-support = ["gpui/test-support", "fs/test-support"] [dependencies] -assets = { path = "../assets" } collections = { path = "../collections" } gpui = { path = "../gpui" } sqlez = { path = "../sqlez" } @@ -25,6 +24,7 @@ futures.workspace = true json_comments = "0.2" lazy_static.workspace = true postage.workspace = true +rust-embed.workspace = true schemars.workspace = true serde.workspace = true serde_derive.workspace = true diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 0b638da924..e607a254bd 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -1,6 +1,5 @@ -use crate::settings_store::parse_json_with_comments; +use crate::{settings_store::parse_json_with_comments, SettingsAssets}; use anyhow::{Context, Result}; -use assets::Assets; use collections::BTreeMap; use gpui::{keymap_matcher::Binding, AppContext}; use schemars::{ @@ -10,11 +9,11 @@ use schemars::{ }; use serde::Deserialize; use serde_json::{value::RawValue, Value}; -use util::ResultExt; +use util::{asset_str, ResultExt}; #[derive(Deserialize, Default, Clone, JsonSchema)] #[serde(transparent)] -pub struct KeymapFileContent(Vec); +pub struct KeymapFile(Vec); #[derive(Deserialize, Default, Clone, JsonSchema)] pub struct KeymapBlock { @@ -40,11 +39,10 @@ impl JsonSchema for KeymapAction { #[derive(Deserialize)] struct ActionWithData(Box, Box); -impl KeymapFileContent { +impl KeymapFile { pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> { - let content = Assets::get(asset_path).unwrap().data; - let content_str = std::str::from_utf8(content.as_ref()).unwrap(); - Self::parse(content_str)?.add_to_cx(cx) + let content = asset_str::(asset_path); + Self::parse(content.as_ref())?.add_to_cx(cx) } pub fn parse(content: &str) -> Result { @@ -83,40 +81,40 @@ impl KeymapFileContent { } Ok(()) } -} -pub fn keymap_file_json_schema(action_names: &[&'static str]) -> serde_json::Value { - let mut root_schema = SchemaSettings::draft07() - .with(|settings| settings.option_add_null_type = false) - .into_generator() - .into_root_schema_for::(); + pub fn generate_json_schema(action_names: &[&'static str]) -> serde_json::Value { + let mut root_schema = SchemaSettings::draft07() + .with(|settings| settings.option_add_null_type = false) + .into_generator() + .into_root_schema_for::(); - let action_schema = Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - one_of: Some(vec![ - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - enum_values: Some( - action_names - .iter() - .map(|name| Value::String(name.to_string())) - .collect(), - ), - ..Default::default() - }), - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), - ..Default::default() - }), - ]), + let action_schema = Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![ + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some( + action_names + .iter() + .map(|name| Value::String(name.to_string())) + .collect(), + ), + ..Default::default() + }), + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), + ..Default::default() + }), + ]), + ..Default::default() + })), ..Default::default() - })), - ..Default::default() - }); + }); - root_schema - .definitions - .insert("KeymapAction".to_owned(), action_schema); + root_schema + .definitions + .insert("KeymapAction".to_owned(), action_schema); - serde_json::to_value(root_schema).unwrap() + serde_json::to_value(root_schema).unwrap() + } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 1c131e5916..8c3587d942 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -2,31 +2,37 @@ mod keymap_file; mod settings_file; mod settings_store; -use gpui::AssetSource; -pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; +use rust_embed::RustEmbed; +use std::{borrow::Cow, str}; +use util::asset_str; + +pub use keymap_file::KeymapFile; pub use settings_file::*; pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; -use std::{borrow::Cow, str}; -pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; -const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; -const INITIAL_LOCAL_SETTINGS_ASSET_PATH: &str = "settings/initial_local_settings.json"; +#[derive(RustEmbed)] +#[folder = "../../assets"] +#[include = "settings/*"] +#[include = "keymaps/*"] +#[exclude = "*.DS_Store"] +pub struct SettingsAssets; pub fn default_settings() -> Cow<'static, str> { - asset_str(&assets::Assets, DEFAULT_SETTINGS_ASSET_PATH) + asset_str::("settings/default.json") } -pub fn initial_user_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { - asset_str(assets, INITIAL_USER_SETTINGS_ASSET_PATH) +pub fn default_keymap() -> Cow<'static, str> { + asset_str::("keymaps/default.json") } -pub fn initial_local_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { - asset_str(assets, INITIAL_LOCAL_SETTINGS_ASSET_PATH) +pub fn vim_keymap() -> Cow<'static, str> { + asset_str::("keymaps/vim.json") } -fn asset_str<'a>(assets: &'a dyn AssetSource, path: &str) -> Cow<'a, str> { - match assets.load(path).unwrap() { - Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), - Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), - } +pub fn initial_user_settings_content() -> Cow<'static, str> { + asset_str::("settings/initial_user_settings.json") +} + +pub fn initial_local_settings_content() -> Cow<'static, str> { + asset_str::("settings/initial_local_settings.json") } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index edd4fe0d9d..c7ae296469 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,6 +1,5 @@ use crate::{settings_store::SettingsStore, Setting}; use anyhow::Result; -use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{executor::Background, AppContext}; @@ -111,7 +110,7 @@ async fn load_settings(fs: &Arc) -> Result { Err(err) => { if let Some(e) = err.downcast_ref::() { if e.kind() == ErrorKind::NotFound { - return Ok(crate::initial_user_settings_content(&Assets).to_string()); + return Ok(crate::initial_user_settings_content().to_string()); } } return Err(err); diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 6216d2e472..8d9594fbeb 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -21,6 +21,7 @@ isahc.workspace = true smol.workspace = true url = "2.2" rand.workspace = true +rust-embed.workspace = true tempdir = { workspace = true, optional = true } serde.workspace = true serde_json.workspace = true diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 5f738132ae..79f3c68514 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -7,6 +7,7 @@ pub mod paths; pub mod test; use std::{ + borrow::Cow, cmp::{self, Ordering}, ops::{AddAssign, Range, RangeInclusive}, panic::Location, @@ -284,6 +285,14 @@ impl Iterator for RandomCharIter { } } +/// Get an embedded file as a string. +pub fn asset_str(path: &str) -> Cow<'static, str> { + match A::get(path).unwrap().data { + Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).unwrap()), + Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).unwrap()), + } +} + // copy unstable standard feature option unzip // https://github.com/rust-lang/rust/issues/87800 // Remove when this ship in Rust 1.66 or 1.67 diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index ee3144fd56..57d3821379 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -24,7 +24,6 @@ nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", f tokio = { version = "1.15", "optional" = true } serde_json.workspace = true -assets = { path = "../assets" } collections = { path = "../collections" } command_palette = { path = "../command_palette" } editor = { path = "../editor" } diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 531fbf0bba..3e66d6bb1c 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -27,7 +27,7 @@ impl<'a> VimTestContext<'a> { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| *s = Some(enabled)); }); - settings::KeymapFileContent::load_asset("keymaps/vim.json", cx).unwrap(); + settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap(); }); // Setup search toolbars and keypress hook diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index b22607e20d..8606be4944 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -19,7 +19,6 @@ test-support = [ ] [dependencies] -assets = { path = "../assets" } db = { path = "../db" } call = { path = "../call" } client = { path = "../client" } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c45991cf11..8add2e71c1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -17,7 +17,6 @@ path = "src/main.rs" [dependencies] activity_indicator = { path = "../activity_indicator" } -assets = { path = "../assets" } auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } call = { path = "../call" } diff --git a/crates/zed/build.rs b/crates/zed/build.rs index 14bf9999fb..b83afba747 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -1,3 +1,5 @@ +use std::process::Command; + fn main() { println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); @@ -21,4 +23,32 @@ fn main() { // Register exported Objective-C selectors, protocols, etc println!("cargo:rustc-link-arg=-Wl,-ObjC"); + + // Install dependencies for theme-generation + let output = Command::new("npm") + .current_dir("../../styles") + .args(["install", "--no-save"]) + .output() + .expect("failed to run npm"); + if !output.status.success() { + panic!( + "failed to install theme dependencies {}", + String::from_utf8_lossy(&output.stderr) + ); + } + + // Regenerate themes + let output = Command::new("npm") + .current_dir("../../styles") + .args(["run", "build"]) + .output() + .expect("failed to run npm"); + if !output.status.success() { + panic!( + "build script failed {}", + String::from_utf8_lossy(&output.stderr) + ); + } + + println!("cargo:rerun-if-changed=../../styles/src"); } diff --git a/crates/assets/src/assets.rs b/crates/zed/src/assets.rs similarity index 84% rename from crates/assets/src/assets.rs rename to crates/zed/src/assets.rs index 7d5748e43b..6eb8a44f0f 100644 --- a/crates/assets/src/assets.rs +++ b/crates/zed/src/assets.rs @@ -4,6 +4,10 @@ use rust_embed::RustEmbed; #[derive(RustEmbed)] #[folder = "../../assets"] +#[include = "fonts/**/*"] +#[include = "icons/**/*"] +#[include = "themes/**/*"] +#[include = "*.md"] #[exclude = "*.DS_Store"] pub struct Assets; diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 1f2b359af1..3ae564d13b 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -3,6 +3,7 @@ pub use language::*; use node_runtime::NodeRuntime; use rust_embed::RustEmbed; use std::{borrow::Cow, str, sync::Arc}; +use util::asset_str; mod c; mod elixir; @@ -179,10 +180,7 @@ fn load_query(name: &str, filename_prefix: &str) -> Option> { for path in LanguageDir::iter() { if let Some(remainder) = path.strip_prefix(name) { if remainder.starts_with(filename_prefix) { - let contents = match LanguageDir::get(path.as_ref()).unwrap().data { - Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), - Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), - }; + let contents = asset_str::(path.as_ref()); match &mut result { None => result = Some(contents), Some(r) => r.to_mut().push_str(contents.as_ref()), diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 3947c62a6d..e1f3da9e02 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -6,7 +6,7 @@ use gpui::AppContext; use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter}; use node_runtime::NodeRuntime; use serde_json::json; -use settings::{keymap_file_json_schema, SettingsJsonSchemaParams, SettingsStore}; +use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; use smol::fs; use staff_mode::StaffMode; use std::{ @@ -143,7 +143,7 @@ impl LspAdapter for JsonLspAdapter { }, { "fileMatch": [schema_file_match(&paths::KEYMAP)], - "schema": keymap_file_json_schema(&action_names), + "schema": KeymapFile::generate_json_schema(&action_names), } ] } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f5031beb2e..03b7eb6b24 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -2,7 +2,6 @@ #![allow(non_snake_case)] use anyhow::{anyhow, Context, Result}; -use assets::Assets; use backtrace::Backtrace; use cli::{ ipc::{self, IpcSender}, @@ -58,7 +57,8 @@ use staff_mode::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace}; use zed::{ - self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus, + assets::Assets, build_window_options, handle_keymap_file_changes, initialize_workspace, + languages, menus, }; fn main() { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 8d27a378c5..a7bd820350 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,7 +1,9 @@ +pub mod assets; pub mod languages; pub mod menus; #[cfg(any(test, feature = "test-support"))] pub mod test; + use anyhow::Context; use assets::Assets; use breadcrumbs::Breadcrumbs; @@ -30,12 +32,11 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{ - initial_local_settings_content, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, -}; +use settings::{initial_local_settings_content, KeymapFile, SettingsStore}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_panel::{self, TerminalPanel}; use util::{ + asset_str, channel::ReleaseChannel, paths::{self, LOCAL_SETTINGS_RELATIVE_PATH}, ResultExt, @@ -149,7 +150,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { open_bundled_file( workspace, - "licenses.md", + asset_str::("licenses.md"), "Open Source License Attribution", "Markdown", cx, @@ -169,9 +170,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx.add_action( move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { create_and_open_local_file(&paths::SETTINGS, cx, || { - settings::initial_user_settings_content(&Assets) - .as_ref() - .into() + settings::initial_user_settings_content().as_ref().into() }) .detach_and_log_err(cx); }, @@ -181,7 +180,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( workspace, - "keymaps/default.json", + settings::default_keymap(), "Default Key Bindings", "JSON", cx, @@ -194,7 +193,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx: &mut ViewContext| { open_bundled_file( workspace, - DEFAULT_SETTINGS_ASSET_PATH, + settings::default_settings(), "Default Settings", "JSON", cx, @@ -521,11 +520,11 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { pub fn load_default_keymap(cx: &mut AppContext) { for path in ["keymaps/default.json", "keymaps/vim.json"] { - KeymapFileContent::load_asset(path, cx).unwrap(); + KeymapFile::load_asset(path, cx).unwrap(); } if let Some(asset_path) = settings::get::(cx).asset_path() { - KeymapFileContent::load_asset(asset_path, cx).unwrap(); + KeymapFile::load_asset(asset_path, cx).unwrap(); } } @@ -536,7 +535,7 @@ pub fn handle_keymap_file_changes( cx.spawn(move |mut cx| async move { let mut settings_subscription = None; while let Some(user_keymap_content) = user_keymap_file_rx.next().await { - if let Ok(keymap_content) = KeymapFileContent::parse(&user_keymap_content) { + if let Ok(keymap_content) = KeymapFile::parse(&user_keymap_content) { cx.update(|cx| { cx.clear_bindings(); load_default_keymap(cx); @@ -613,11 +612,7 @@ fn open_local_settings_file( if let Some(buffer) = editor.buffer().read(cx).as_singleton() { if buffer.read(cx).is_empty() { buffer.update(cx, |buffer, cx| { - buffer.edit( - [(0..0, initial_local_settings_content(&Assets))], - None, - cx, - ) + buffer.edit([(0..0, initial_local_settings_content())], None, cx) }); } } @@ -693,7 +688,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext, title: &'static str, language: &'static str, cx: &mut ViewContext, @@ -705,13 +700,9 @@ fn open_bundled_file( .update(&mut cx, |workspace, cx| { workspace.with_local_workspace(cx, |workspace, cx| { let project = workspace.project(); - let buffer = project.update(cx, |project, cx| { - let text = Assets::get(asset_path) - .map(|f| f.data) - .unwrap_or_else(|| Cow::Borrowed(b"File not found")); - let text = str::from_utf8(text.as_ref()).unwrap(); + let buffer = project.update(cx, move |project, cx| { project - .create_buffer(text, language, cx) + .create_buffer(text.as_ref(), language, cx) .expect("creating buffers on a local workspace always succeeds") }); let buffer = cx.add_model(|cx| { From 572c59eec4225c790f98597ad4255d880aa98793 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 6 Jun 2023 21:50:08 +0200 Subject: [PATCH 100/131] chore: Enable full LTO (#2548) Per conversation with Antonio, I've suggested enabling full LTO; right now we use a crate-local ThinLTO, which does not inline function calls across crates. | Configuration | Current main (788f97e) | Thin LTO | Full LTO | |------------------|------------------------|-----------|-----------| | Size in bytes | 158806721 | 155868753 | 111115553 | | % of `main` size | 100% | 98.14% | 69.96% | | Size in bytes (no debug info) | 129186657 | 127942929 | 108281345 | --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index bc614b6783..72a93177a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,3 +116,4 @@ split-debuginfo = "unpacked" [profile.release] debug = true +lto = "thin" From c1c91dc2e3133a3de774a3ee01e9047d6dee8320 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 6 Jun 2023 21:50:27 +0200 Subject: [PATCH 101/131] chore: bump MSRV to 1.70 (#2573) Add rust-toolchain.toml Release Notes: - N/A --- .github/workflows/ci.yml | 1 + Dockerfile | 2 +- rust-toolchain.toml | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1c16b2d4d..215f7ba165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - name: Install Rust run: | rustup set profile minimal + rustup component add rustfmt rustup update stable - name: Checkout repo diff --git a/Dockerfile b/Dockerfile index d3170696c5..2a78d37cbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.65-bullseye as builder +FROM rust:1.70-bullseye as builder WORKDIR app COPY . . diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..f400973ca7 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.70" From 559a58d7371c849d038ed452cefb92618e46b871 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 6 Jun 2023 22:21:34 +0200 Subject: [PATCH 102/131] Revert "chore: bump MSRV to 1.70" (#2577) Reverts zed-industries/zed#2573 --- .github/workflows/ci.yml | 1 - Dockerfile | 2 +- rust-toolchain.toml | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 215f7ba165..f1c16b2d4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,6 @@ jobs: - name: Install Rust run: | rustup set profile minimal - rustup component add rustfmt rustup update stable - name: Checkout repo diff --git a/Dockerfile b/Dockerfile index 2a78d37cbb..d3170696c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.70-bullseye as builder +FROM rust:1.65-bullseye as builder WORKDIR app COPY . . diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index f400973ca7..0000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "1.70" From 7bfb51ee76c9def7125352e5e5da181e859c442e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 14:20:21 -0700 Subject: [PATCH 103/131] Live-reload tree-sitter queries in development --- crates/language/src/language.rs | 56 +++++++++++++++++++++++++++------ crates/project/src/project.rs | 27 ++++++++++++++-- crates/zed/src/main.rs | 21 +++++++++++++ 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8b4041b852..19ae948574 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -34,7 +34,7 @@ use std::{ fmt::Debug, hash::Hash, mem, - ops::Range, + ops::{Not, Range}, path::{Path, PathBuf}, str, sync::{ @@ -500,6 +500,7 @@ struct AvailableLanguage { grammar: tree_sitter::Language, lsp_adapters: Vec>, get_queries: fn(&str) -> LanguageQueries, + loaded: bool, } pub struct LanguageRegistry { @@ -527,6 +528,7 @@ struct LanguageRegistryState { subscription: (watch::Sender<()>, watch::Receiver<()>), theme: Option>, version: usize, + reload_count: usize, } pub struct PendingLanguageServer { @@ -547,6 +549,7 @@ impl LanguageRegistry { subscription: watch::channel(), theme: Default::default(), version: 0, + reload_count: 0, }), language_server_download_dir: None, lsp_binary_statuses_tx, @@ -566,6 +569,14 @@ impl LanguageRegistry { self.executor = Some(executor); } + /// Clear out all of the loaded languages and reload them from scratch. + /// + /// This is useful in development, when queries have changed. + #[cfg(debug_assertions)] + pub fn reload(&self) { + self.state.write().reload(); + } + pub fn register( &self, path: &'static str, @@ -582,6 +593,7 @@ impl LanguageRegistry { grammar, lsp_adapters, get_queries, + loaded: false, }); } @@ -590,7 +602,7 @@ impl LanguageRegistry { let mut result = state .available_languages .iter() - .map(|l| l.config.name.to_string()) + .filter_map(|l| l.loaded.not().then_some(l.config.name.to_string())) .chain(state.languages.iter().map(|l| l.config.name.to_string())) .collect::>(); result.sort_unstable_by_key(|language_name| language_name.to_lowercase()); @@ -603,6 +615,7 @@ impl LanguageRegistry { state .available_languages .iter() + .filter(|l| !l.loaded) .flat_map(|l| l.lsp_adapters.clone()) .chain( state @@ -639,10 +652,17 @@ impl LanguageRegistry { self.state.read().subscription.1.clone() } + /// The number of times that the registry has been changed, + /// by adding languages or reloading. pub fn version(&self) -> usize { self.state.read().version } + /// The number of times that the registry has been reloaded. + pub fn reload_count(&self) -> usize { + self.state.read().reload_count + } + pub fn set_theme(&self, theme: Arc) { let mut state = self.state.write(); state.theme = Some(theme.clone()); @@ -721,7 +741,7 @@ impl LanguageRegistry { if let Some(language) = state .available_languages .iter() - .find(|l| callback(&l.config)) + .find(|l| !l.loaded && callback(&l.config)) .cloned() { let txs = state @@ -743,9 +763,7 @@ impl LanguageRegistry { let language = Arc::new(language); let mut state = this.state.write(); state.add(language.clone()); - state - .available_languages - .retain(|language| language.id != id); + state.mark_language_loaded(id); if let Some(mut txs) = state.loading_languages.remove(&id) { for tx in txs.drain(..) { let _ = tx.send(Ok(language.clone())); @@ -754,9 +772,7 @@ impl LanguageRegistry { } Err(err) => { let mut state = this.state.write(); - state - .available_languages - .retain(|language| language.id != id); + state.mark_language_loaded(id); if let Some(mut txs) = state.loading_languages.remove(&id) { for tx in txs.drain(..) { let _ = tx.send(Err(anyhow!( @@ -905,6 +921,28 @@ impl LanguageRegistryState { self.version += 1; *self.subscription.0.borrow_mut() = (); } + + #[cfg(debug_assertions)] + fn reload(&mut self) { + self.languages.clear(); + self.version += 1; + self.reload_count += 1; + for language in &mut self.available_languages { + language.loaded = false; + } + *self.subscription.0.borrow_mut() = (); + } + + /// Mark the given language a having been loaded, so that the + /// language registry won't try to load it again. + fn mark_language_loaded(&mut self, id: AvailableLanguageId) { + for language in &mut self.available_languages { + if language.id == id { + language.loaded = true; + break; + } + } + } } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b0d0d670ba..39d8ea8512 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -523,7 +523,7 @@ impl Project { _subscriptions: vec![ cx.observe_global::(Self::on_settings_changed) ], - _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), + _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx), _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), active_entry: None, languages, @@ -592,7 +592,7 @@ impl Project { active_entry: None, collaborators: Default::default(), join_project_response_message_id: response.message_id, - _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), + _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx), _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), languages, user_store: user_store.clone(), @@ -2238,13 +2238,34 @@ impl Project { } fn maintain_buffer_languages( - languages: &LanguageRegistry, + languages: Arc, cx: &mut ModelContext, ) -> Task<()> { let mut subscription = languages.subscribe(); + let mut prev_reload_count = languages.reload_count(); cx.spawn_weak(|project, mut cx| async move { while let Some(()) = subscription.next().await { if let Some(project) = project.upgrade(&cx) { + // If the language registry has been reloaded, then remove and + // re-assign the languages on all open buffers. + let reload_count = languages.reload_count(); + if reload_count > prev_reload_count { + prev_reload_count = reload_count; + project.update(&mut cx, |this, cx| { + let buffers = this + .opened_buffers + .values() + .filter_map(|b| b.upgrade(cx)) + .collect::>(); + for buffer in buffers { + if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned() { + this.unregister_buffer_from_language_servers(&buffer, &f, cx); + buffer.update(cx, |buffer, cx| buffer.set_language(None, cx)); + } + } + }); + } + project.update(&mut cx, |project, cx| { let mut plain_text_buffers = Vec::new(); let mut buffers_with_unknown_injections = Vec::new(); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 03b7eb6b24..2393d0df3b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -160,6 +160,8 @@ fn main() { ai::init(cx); cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach(); + cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) + .detach(); languages.set_theme(theme::current(cx).clone()); cx.observe_global::({ @@ -660,11 +662,30 @@ async fn watch_themes(fs: Arc, mut cx: AsyncAppContext) -> Option<()> { Some(()) } +#[cfg(debug_assertions)] +async fn watch_languages(fs: Arc, languages: Arc) -> Option<()> { + let mut events = fs + .watch( + "crates/zed/src/languages".as_ref(), + Duration::from_millis(100), + ) + .await; + while (events.next().await).is_some() { + languages.reload(); + } + Some(()) +} + #[cfg(not(debug_assertions))] async fn watch_themes(_fs: Arc, _cx: AsyncAppContext) -> Option<()> { None } +#[cfg(not(debug_assertions))] +async fn watch_languages(_: Arc, _: Arc) -> Option<()> { + None +} + fn connect_to_cli( server_name: &str, ) -> Result<(mpsc::Receiver, IpcSender)> { From 3fc2e0754bb0b95da7e16b949707e998d925e6fe Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 14:27:23 -0700 Subject: [PATCH 104/131] Don't apply the 'literal' color to entire markdown code blocks --- crates/zed/src/languages/markdown/highlights.scm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/zed/src/languages/markdown/highlights.scm b/crates/zed/src/languages/markdown/highlights.scm index 83bf7b57a4..971c276868 100644 --- a/crates/zed/src/languages/markdown/highlights.scm +++ b/crates/zed/src/languages/markdown/highlights.scm @@ -14,11 +14,11 @@ (list_marker_parenthesis) ] @punctuation.list_marker -[ - (indented_code_block) - (fenced_code_block) - (code_span) -] @text.literal +(code_span) @text.literal + +(fenced_code_block + (info_string + (language) @text.literal)) (link_destination) @link_uri (link_text) @link_text From dfd72770e76eea1e99f11fdaa32303bf3b65d7ee Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 6 Jun 2023 23:49:34 +0200 Subject: [PATCH 105/131] chore: bump MSRV to 1.70, add rust-toolchain (#2580) This time I've added a `components` section to rust-toolchain.toml file to explicitly require a rustfmt component. Fingers crossed. --- Dockerfile | 2 +- rust-toolchain.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 rust-toolchain.toml diff --git a/Dockerfile b/Dockerfile index d3170696c5..2a78d37cbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.65-bullseye as builder +FROM rust:1.70-bullseye as builder WORKDIR app COPY . . diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..7046039a2c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.70" +components = [ "rustfmt" ] From cc63d3d048e37da73bdccbdbce3a7583703ced50 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:12:47 +0200 Subject: [PATCH 106/131] chore: add targets to rust-toolchain.toml (#2581) --- rust-toolchain.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7046039a2c..f78a67ddb3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,4 @@ [toolchain] channel = "1.70" components = [ "rustfmt" ] +targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "wasm32-wasi" ] From 6737ee14958dacdaea5d5ec3015ae5798847574d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 16:04:27 -0700 Subject: [PATCH 107/131] Avoid panic when failing to load a language's queries --- crates/language/src/language.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 19ae948574..32d74c9aef 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -771,6 +771,7 @@ impl LanguageRegistry { } } Err(err) => { + log::error!("failed to load language {name} - {err}"); let mut state = this.state.write(); state.mark_language_loaded(id); if let Some(mut txs) = state.loading_languages.remove(&id) { @@ -1059,34 +1060,22 @@ impl Language { pub fn with_queries(mut self, queries: LanguageQueries) -> Result { if let Some(query) = queries.highlights { - self = self - .with_highlights_query(query.as_ref()) - .expect("failed to evaluate highlights query"); + self = self.with_highlights_query(query.as_ref())?; } if let Some(query) = queries.brackets { - self = self - .with_brackets_query(query.as_ref()) - .expect("failed to load brackets query"); + self = self.with_brackets_query(query.as_ref())?; } if let Some(query) = queries.indents { - self = self - .with_indents_query(query.as_ref()) - .expect("failed to load indents query"); + self = self.with_indents_query(query.as_ref())?; } if let Some(query) = queries.outline { - self = self - .with_outline_query(query.as_ref()) - .expect("failed to load outline query"); + self = self.with_outline_query(query.as_ref())?; } if let Some(query) = queries.injections { - self = self - .with_injection_query(query.as_ref()) - .expect("failed to load injection query"); + self = self.with_injection_query(query.as_ref())?; } if let Some(query) = queries.overrides { - self = self - .with_override_query(query.as_ref()) - .expect("failed to load override query"); + self = self.with_override_query(query.as_ref())?; } Ok(self) } From 8dc679e74e8a1d30b2ad1173d3abfbc8a688ddc9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 16:04:46 -0700 Subject: [PATCH 108/131] Upgrade tree-sitter-elixir --- Cargo.lock | 4 ++-- crates/zed/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1ba2a4998..6dbfa27309 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7342,8 +7342,8 @@ dependencies = [ [[package]] name = "tree-sitter-elixir" -version = "0.19.0" -source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=05e3631c6a0701c1fa518b0fee7be95a2ceef5e2#05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" +version = "0.1.0" +source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=4ba9dab6e2602960d95b2b625f3386c27e08084e#4ba9dab6e2602960d95b2b625f3386c27e08084e" dependencies = [ "cc", "tree-sitter", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 8add2e71c1..54919445cb 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -106,7 +106,7 @@ tree-sitter = "0.20" tree-sitter-c = "0.20.1" tree-sitter-cpp = "0.20.0" tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" } -tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" } +tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "4ba9dab6e2602960d95b2b625f3386c27e08084e" } tree-sitter-embedded-template = "0.20.0" tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" } From 7aeaa846579a1588747e748e3b65e922e6af4597 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 15:12:07 -0700 Subject: [PATCH 109/131] Fix the order of some patterns in elixir highlight query --- .../zed/src/languages/elixir/highlights.scm | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/crates/zed/src/languages/elixir/highlights.scm b/crates/zed/src/languages/elixir/highlights.scm index 5c256f341c..deea51c436 100644 --- a/crates/zed/src/languages/elixir/highlights.scm +++ b/crates/zed/src/languages/elixir/highlights.scm @@ -1,20 +1,5 @@ ["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword -(unary_operator - operator: "@" @comment.doc - operand: (call - target: (identifier) @comment.doc.__attribute__ - (arguments - [ - (string) @comment.doc - (charlist) @comment.doc - (sigil - quoted_start: _ @comment.doc - quoted_end: _ @comment.doc) @comment.doc - (boolean) @comment.doc - ])) - (#match? @comment.doc.__attribute__ "^(moduledoc|typedoc|doc)$")) - (unary_operator operator: "&" operand: (integer) @operator) @@ -84,6 +69,11 @@ quoted_start: _ @string.special quoted_end: _ @string.special) @string.special +( + (identifier) @comment.unused + (#match? @comment.unused "^_") +) + (call target: [ (identifier) @function @@ -99,17 +89,12 @@ (binary_operator left: (identifier) @function operator: "when") + (binary_operator + operator: "|>" + right: (identifier)) ]) (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) -(call - target: (identifier) @keyword - (arguments - (binary_operator - operator: "|>" - right: (identifier))) - (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) - (binary_operator operator: "|>" right: (identifier) @function) @@ -127,10 +112,18 @@ (#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$") ) -( - (identifier) @comment.unused - (#match? @comment.unused "^_") -) +(unary_operator + operator: "@" @comment.doc + operand: (call + target: (identifier) @__attribute__ @comment.doc + (arguments + [ + (string) + (charlist) + (sigil) + (boolean) + ] @comment.doc)) + (#match? @__attribute__ "^(moduledoc|typedoc|doc)$")) (comment) @comment From 4b9a3c66e6e51716d3b16912ad6a60c9724674e2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 16:06:01 -0700 Subject: [PATCH 110/131] Show function parameters in elixir outline view Introduce a new capture in the outline query called 'context.extra', which causes text to appear in the outline, but not in the breadcrumbs. --- crates/language/src/buffer.rs | 13 ++++-- crates/language/src/buffer_tests.rs | 46 +++++++++++++++++++++ crates/language/src/language.rs | 4 ++ crates/zed/src/languages/elixir/indents.scm | 4 +- crates/zed/src/languages/elixir/outline.scm | 14 ++++++- crates/zed/src/languages/typescript.rs | 6 +-- 6 files changed, 76 insertions(+), 11 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 7acb36a92f..e09ee48da6 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2253,7 +2253,7 @@ impl BufferSnapshot { } pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option> { - self.outline_items_containing(0..self.len(), theme) + self.outline_items_containing(0..self.len(), true, theme) .map(Outline::new) } @@ -2265,6 +2265,7 @@ impl BufferSnapshot { let position = position.to_offset(self); let mut items = self.outline_items_containing( position.saturating_sub(1)..self.len().min(position + 1), + false, theme, )?; let mut prev_depth = None; @@ -2279,6 +2280,7 @@ impl BufferSnapshot { fn outline_items_containing( &self, range: Range, + include_extra_context: bool, theme: Option<&SyntaxTheme>, ) -> Option>> { let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| { @@ -2313,7 +2315,10 @@ impl BufferSnapshot { let node_is_name; if capture.index == config.name_capture_ix { node_is_name = true; - } else if Some(capture.index) == config.context_capture_ix { + } else if Some(capture.index) == config.context_capture_ix + || (Some(capture.index) == config.extra_context_capture_ix + && include_extra_context) + { node_is_name = false; } else { continue; @@ -2340,10 +2345,12 @@ impl BufferSnapshot { buffer_ranges.first().unwrap().0.start..buffer_ranges.last().unwrap().0.end, true, ); + let mut last_buffer_range_end = 0; for (buffer_range, is_name) in buffer_ranges { - if !text.is_empty() { + if !text.is_empty() && buffer_range.start > last_buffer_range_end { text.push(' '); } + last_buffer_range_end = buffer_range.end; if is_name { let mut start = text.len(); let end = start + buffer_range.len(); diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index be573aa895..9f44de40ac 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -592,6 +592,52 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) { ); } +#[gpui::test] +async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) { + let language = javascript_lang() + .with_outline_query( + r#" + (function_declaration + "function" @context + name: (_) @name + parameters: (formal_parameters + "(" @context.extra + ")" @context.extra)) @item + "#, + ) + .unwrap(); + + let text = r#" + function a() {} + function b(c) {} + "# + .unindent(); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(language), cx)); + let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot()); + + // extra context nodes are included in the outline. + let outline = snapshot.outline(None).unwrap(); + assert_eq!( + outline + .items + .iter() + .map(|item| (item.text.as_str(), item.depth)) + .collect::>(), + &[("function a()", 0), ("function b( )", 0),] + ); + + // extra context nodes do not appear in breadcrumbs. + let symbols = snapshot.symbols_containing(3, None).unwrap(); + assert_eq!( + symbols + .iter() + .map(|item| (item.text.as_str(), item.depth)) + .collect::>(), + &[("function a", 0)] + ); +} + #[gpui::test] async fn test_symbols_containing(cx: &mut gpui::TestAppContext) { let text = r#" diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 32d74c9aef..0ff1d973d3 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -455,6 +455,7 @@ struct OutlineConfig { item_capture_ix: u32, name_capture_ix: u32, context_capture_ix: Option, + extra_context_capture_ix: Option, } struct InjectionConfig { @@ -1091,12 +1092,14 @@ impl Language { let mut item_capture_ix = None; let mut name_capture_ix = None; let mut context_capture_ix = None; + let mut extra_context_capture_ix = None; get_capture_indices( &query, &mut [ ("item", &mut item_capture_ix), ("name", &mut name_capture_ix), ("context", &mut context_capture_ix), + ("context.extra", &mut extra_context_capture_ix), ], ); if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) { @@ -1105,6 +1108,7 @@ impl Language { item_capture_ix, name_capture_ix, context_capture_ix, + extra_context_capture_ix, }); } Ok(self) diff --git a/crates/zed/src/languages/elixir/indents.scm b/crates/zed/src/languages/elixir/indents.scm index e4139841fc..ab6fc4da67 100644 --- a/crates/zed/src/languages/elixir/indents.scm +++ b/crates/zed/src/languages/elixir/indents.scm @@ -1,6 +1,4 @@ -[ - (call) -] @indent +(call) @indent (_ "[" "]" @end) @indent (_ "{" "}" @end) @indent diff --git a/crates/zed/src/languages/elixir/outline.scm b/crates/zed/src/languages/elixir/outline.scm index 985c8ffdca..a3311fb6d4 100644 --- a/crates/zed/src/languages/elixir/outline.scm +++ b/crates/zed/src/languages/elixir/outline.scm @@ -8,9 +8,19 @@ (arguments [ (identifier) @name - (call target: (identifier) @name) + (call + target: (identifier) @name + (arguments + "(" @context.extra + _* @context.extra + ")" @context.extra)) (binary_operator - left: (call target: (identifier) @name) + left: (call + target: (identifier) @name + (arguments + "(" @context.extra + _* @context.extra + ")" @context.extra)) operator: "when") ]) (#match? @context "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index a25d7d02ea..7d2d580857 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -327,10 +327,10 @@ mod tests { .map(|item| (item.text.as_str(), item.depth)) .collect::>(), &[ - ("function a ( )", 0), - ("async function a2 ( )", 1), + ("function a()", 0), + ("async function a2()", 1), ("let b", 0), - ("function getB ( )", 0), + ("function getB()", 0), ("const d", 0), ] ); From fccbac4887c2a420679f08eb24e69f78e83a1e2e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 17:12:24 -0700 Subject: [PATCH 111/131] Handle LSP codeActions capability set to false --- crates/project/src/lsp_command.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 490f3bde17..94a43d79ec 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1499,7 +1499,11 @@ impl LspCommand for GetCodeActions { type ProtoRequest = proto::GetCodeActions; fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool { - capabilities.code_action_provider.is_some() + match &capabilities.code_action_provider { + None => false, + Some(lsp::CodeActionProviderCapability::Simple(false)) => false, + _ => true, + } } fn to_lsp( From 6f2726524e49ebd02d33da3145356bea25982790 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 17:13:23 -0700 Subject: [PATCH 112/131] Remove stray println --- crates/editor/src/hover_popover.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 9192dc75e1..4c339e52df 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -588,10 +588,7 @@ impl InfoPopover { MouseRegion::new::(view_id, region_id, bounds) .on_click::( MouseButton::Left, - move |_, _, cx| { - println!("clicked link {url}"); - cx.platform().open_url(&url); - }, + move |_, _, cx| cx.platform().open_url(&url), ), ); } From e8479f23f9e0a43d1eb4c91c935950cf035c01da Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Jun 2023 17:13:49 -0700 Subject: [PATCH 113/131] Improve log message on invalid LSP message from server's stdout --- crates/lsp/src/lsp.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 1b01660308..691203d5e8 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -260,9 +260,10 @@ impl LanguageServer { buffer.clear(); stdout.read_until(b'\n', &mut buffer).await?; stdout.read_until(b'\n', &mut buffer).await?; - let message_len: usize = std::str::from_utf8(&buffer)? + let header = std::str::from_utf8(&buffer)?; + let message_len: usize = header .strip_prefix(CONTENT_LEN_HEADER) - .ok_or_else(|| anyhow!("invalid header"))? + .ok_or_else(|| anyhow!("invalid LSP message header {header:?}"))? .trim_end() .parse()?; @@ -301,7 +302,7 @@ impl LanguageServer { } } else { warn!( - "Failed to deserialize message:\n{}", + "failed to deserialize LSP message:\n{}", std::str::from_utf8(&buffer)? ); } From c93b6cc599a65a7a074388e5b7577e2cbaf6eaeb Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 7 Jun 2023 00:23:58 -0400 Subject: [PATCH 114/131] Tweak comment wording --- assets/settings/initial_user_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/settings/initial_user_settings.json b/assets/settings/initial_user_settings.json index 1599814f96..2af677da9a 100644 --- a/assets/settings/initial_user_settings.json +++ b/assets/settings/initial_user_settings.json @@ -1,6 +1,6 @@ // Folder-specific settings // -// For information on how to configure folder-specific settings, see the documentation: +// For a full list of overridable settings, and general information on folder-specific settings, see the documentation: // https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings // // To see all of Zed's default settings without changing your From a6feaf1300c3f28f762ed17c5e8d98c3d6a001c4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 7 Jun 2023 09:24:18 +0200 Subject: [PATCH 115/131] Allow search assistant editors --- crates/ai/src/assistant.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 7f387194b3..aee224e420 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -221,6 +221,8 @@ impl AssistantPanel { cx.focus_self(); cx.notify(); } + } else { + cx.propagate_action(); } } @@ -1144,6 +1146,13 @@ impl Item for AssistantEditor { fn tab_tooltip_text(&self, cx: &AppContext) -> Option> { Some(self.title(cx).into()) } + + fn as_searchable( + &self, + _: &ViewHandle, + ) -> Option> { + Some(Box::new(self.editor.clone())) + } } #[derive(Debug)] From 43500dbf6031e5b482e93c4c18ed05dfb0f0fc73 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 7 Jun 2023 10:02:35 +0200 Subject: [PATCH 116/131] Fix zed tests --- crates/zed/src/zed.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f58ea69b93..ecdd1b7a18 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2200,6 +2200,7 @@ mod tests { pane::init(cx); project_panel::init(cx); terminal_view::init(cx); + ai::init(cx); app_state }) } From d26cc2c897732ddb1fe2dac34aabd25e4df6b469 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 7 Jun 2023 15:01:50 +0200 Subject: [PATCH 117/131] Maintain scroll bottom when streaming assistant responses --- crates/ai/src/assistant.rs | 205 +++++++++++++++++++++------- crates/editor/src/editor_tests.rs | 6 +- crates/editor/src/items.rs | 10 +- crates/editor/src/scroll.rs | 22 +-- crates/editor/src/scroll/actions.rs | 6 +- crates/vim/src/normal.rs | 2 +- 6 files changed, 176 insertions(+), 75 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index aee224e420..1a6da539fa 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -5,13 +5,21 @@ use crate::{ use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; use collections::{HashMap, HashSet}; -use editor::{Anchor, Editor, ExcerptId, ExcerptRange, MultiBuffer}; +use editor::{ + display_map::ToDisplayPoint, + scroll::{ + autoscroll::{Autoscroll, AutoscrollStrategy}, + ScrollAnchor, + }, + Anchor, DisplayPoint, Editor, ExcerptId, ExcerptRange, MultiBuffer, +}; use fs::Fs; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use gpui::{ actions, elements::*, executor::Background, + geometry::vector::vec2f, platform::{CursorStyle, MouseButton}, Action, AppContext, AsyncAppContext, ClipboardItem, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, @@ -414,6 +422,7 @@ impl Panel for AssistantPanel { enum AssistantEvent { MessagesEdited { ids: Vec }, SummaryChanged, + StreamedCompletion, } struct Assistant { @@ -531,7 +540,7 @@ impl Assistant { cx.notify(); } - fn assist(&mut self, cx: &mut ModelContext) { + fn assist(&mut self, cx: &mut ModelContext) -> Option<(Message, Message)> { let messages = self .messages .iter() @@ -548,24 +557,30 @@ impl Assistant { stream: true, }; - let api_key = self.api_key.borrow().clone(); - if let Some(api_key) = api_key { - let stream = stream_completion(api_key, cx.background().clone(), request); - let (excerpt_id, content) = - self.insert_message_after(ExcerptId::max(), Role::Assistant, cx); - self.insert_message_after(ExcerptId::max(), Role::User, cx); - let task = cx.spawn_weak(|this, mut cx| async move { + let api_key = self.api_key.borrow().clone()?; + let stream = stream_completion(api_key, cx.background().clone(), request); + let assistant_message = self.insert_message_after(ExcerptId::max(), Role::Assistant, cx); + let user_message = self.insert_message_after(ExcerptId::max(), Role::User, cx); + let task = cx.spawn_weak({ + let assistant_message = assistant_message.clone(); + |this, mut cx| async move { + let assistant_message = assistant_message; let stream_completion = async { let mut messages = stream.await?; while let Some(message) = messages.next().await { let mut message = message?; if let Some(choice) = message.choices.pop() { - content.update(&mut cx, |content, cx| { + assistant_message.content.update(&mut cx, |content, cx| { let text: Arc = choice.delta.content?.into(); content.edit([(content.len()..content.len(), text)], None, cx); Some(()) }); + this.upgrade(&cx) + .ok_or_else(|| anyhow!("assistant was dropped"))? + .update(&mut cx, |_, cx| { + cx.emit(AssistantEvent::StreamedCompletion); + }); } } @@ -580,23 +595,28 @@ impl Assistant { anyhow::Ok(()) }; - if let Err(error) = stream_completion.await { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - if let Some(metadata) = this.messages_metadata.get_mut(&excerpt_id) { + let result = stream_completion.await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + if let Err(error) = result { + if let Some(metadata) = this + .messages_metadata + .get_mut(&assistant_message.excerpt_id) + { metadata.error = Some(error.to_string().trim().into()); cx.notify(); } - }); - } + } + }); } - }); + } + }); - self.pending_completions.push(PendingCompletion { - id: post_inc(&mut self.completion_count), - _task: task, - }); - } + self.pending_completions.push(PendingCompletion { + id: post_inc(&mut self.completion_count), + _task: task, + }); + Some((assistant_message, user_message)) } fn cancel_last_assist(&mut self) -> bool { @@ -646,7 +666,7 @@ impl Assistant { excerpt_id: ExcerptId, role: Role, cx: &mut ModelContext, - ) -> (ExcerptId, ModelHandle) { + ) -> Message { let content = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx); let markdown = self.languages.language_for_name("Markdown"); @@ -684,13 +704,11 @@ impl Assistant { .iter() .position(|message| message.excerpt_id == excerpt_id) .map_or(self.messages.len(), |ix| ix + 1); - self.messages.insert( - ix, - Message { - excerpt_id: new_excerpt_id, - content: content.clone(), - }, - ); + let message = Message { + excerpt_id: new_excerpt_id, + content: content.clone(), + }; + self.messages.insert(ix, message.clone()); self.messages_metadata.insert( new_excerpt_id, MessageMetadata { @@ -699,7 +717,7 @@ impl Assistant { error: None, }, ); - (new_excerpt_id, content) + message } fn summarize(&mut self, cx: &mut ModelContext) { @@ -766,6 +784,7 @@ enum AssistantEditorEvent { struct AssistantEditor { assistant: ModelHandle, editor: ViewHandle, + scroll_bottom: ScrollAnchor, _subscriptions: Vec, } @@ -875,37 +894,64 @@ impl AssistantEditor { let _subscriptions = vec![ cx.observe(&assistant, |_, _, cx| cx.notify()), cx.subscribe(&assistant, Self::handle_assistant_event), + cx.subscribe(&editor, Self::handle_editor_event), ]; Self { assistant, editor, + scroll_bottom: ScrollAnchor { + offset: Default::default(), + anchor: Anchor::max(), + }, _subscriptions, } } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { - self.assistant.update(cx, |assistant, cx| { + let user_message = self.assistant.update(cx, |assistant, cx| { let editor = self.editor.read(cx); let newest_selection = editor.selections.newest_anchor(); let excerpt_id = if newest_selection.head() == Anchor::min() { - assistant.messages.first().map(|message| message.excerpt_id) + assistant + .messages + .first() + .map(|message| message.excerpt_id)? } else if newest_selection.head() == Anchor::max() { - assistant.messages.last().map(|message| message.excerpt_id) + assistant + .messages + .last() + .map(|message| message.excerpt_id)? } else { - Some(newest_selection.head().excerpt_id()) + newest_selection.head().excerpt_id() }; - if let Some(excerpt_id) = excerpt_id { - if let Some(metadata) = assistant.messages_metadata.get(&excerpt_id) { - if metadata.role == Role::User { - assistant.assist(cx); - } else { - assistant.insert_message_after(excerpt_id, Role::User, cx); - } - } - } + let metadata = assistant.messages_metadata.get(&excerpt_id)?; + let user_message = if metadata.role == Role::User { + let (_, user_message) = assistant.assist(cx)?; + user_message + } else { + let user_message = assistant.insert_message_after(excerpt_id, Role::User, cx); + user_message + }; + Some(user_message) }); + + if let Some(user_message) = user_message { + self.editor.update(cx, |editor, cx| { + let cursor = editor + .buffer() + .read(cx) + .snapshot(cx) + .anchor_in_excerpt(user_message.excerpt_id, language::Anchor::MIN); + editor.change_selections( + Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)), + cx, + |selections| selections.select_anchor_ranges([cursor..cursor]), + ); + }); + self.update_scroll_bottom(cx); + } } fn cancel_last_assist(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { @@ -919,7 +965,7 @@ impl AssistantEditor { fn handle_assistant_event( &mut self, - assistant: ModelHandle, + _: ModelHandle, event: &AssistantEvent, cx: &mut ViewContext, ) { @@ -931,16 +977,70 @@ impl AssistantEditor { .map(|selection| selection.head()) .collect::>(); let ids = ids.iter().copied().collect::>(); - assistant.update(cx, |assistant, cx| { + self.assistant.update(cx, |assistant, cx| { assistant.remove_empty_messages(ids, selection_heads, cx) }); } AssistantEvent::SummaryChanged => { cx.emit(AssistantEditorEvent::TabContentChanged); } + AssistantEvent::StreamedCompletion => { + self.editor.update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let scroll_bottom_row = self + .scroll_bottom + .anchor + .to_display_point(&snapshot.display_snapshot) + .row(); + + let scroll_bottom = scroll_bottom_row as f32 + self.scroll_bottom.offset.y(); + let visible_line_count = editor.visible_line_count().unwrap_or(0.); + let scroll_top = scroll_bottom - visible_line_count; + editor + .set_scroll_position(vec2f(self.scroll_bottom.offset.x(), scroll_top), cx); + }); + } } } + fn handle_editor_event( + &mut self, + _: ViewHandle, + event: &editor::Event, + cx: &mut ViewContext, + ) { + match event { + editor::Event::ScrollPositionChanged { .. } => self.update_scroll_bottom(cx), + _ => {} + } + } + + fn update_scroll_bottom(&mut self, cx: &mut ViewContext) { + self.editor.update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let scroll_position = editor + .scroll_manager + .anchor() + .scroll_position(&snapshot.display_snapshot); + let scroll_bottom = scroll_position.y() + editor.visible_line_count().unwrap_or(0.); + let scroll_bottom_point = cmp::min( + DisplayPoint::new(scroll_bottom.floor() as u32, 0), + snapshot.display_snapshot.max_point(), + ); + let scroll_bottom_anchor = snapshot + .buffer_snapshot + .anchor_after(scroll_bottom_point.to_point(&snapshot.display_snapshot)); + let scroll_bottom_offset = vec2f( + scroll_position.x(), + scroll_bottom - scroll_bottom_point.row() as f32, + ); + self.scroll_bottom = ScrollAnchor { + anchor: scroll_bottom_anchor, + offset: scroll_bottom_offset, + }; + }); + } + fn quote_selection( workspace: &mut Workspace, _: &QuoteSelection, @@ -1155,7 +1255,7 @@ impl Item for AssistantEditor { } } -#[derive(Debug)] +#[derive(Clone, Debug)] struct Message { excerpt_id: ExcerptId, content: ModelHandle, @@ -1265,15 +1365,16 @@ mod tests { cx.add_model(|cx| { let mut assistant = Assistant::new(Default::default(), registry, cx); - let (excerpt_1, _) = - assistant.insert_message_after(ExcerptId::max(), Role::Assistant, cx); - let (excerpt_2, _) = assistant.insert_message_after(excerpt_1, Role::User, cx); - let (excerpt_3, _) = assistant.insert_message_after(excerpt_1, Role::User, cx); + let message_1 = assistant.insert_message_after(ExcerptId::max(), Role::Assistant, cx); + let message_2 = assistant.insert_message_after(message_1.excerpt_id, Role::User, cx); + let message_3 = assistant.insert_message_after(message_1.excerpt_id, Role::User, cx); assistant.remove_empty_messages( - HashSet::from_iter([excerpt_2, excerpt_3]), + HashSet::from_iter([message_2.excerpt_id, message_3.excerpt_id]), Default::default(), cx, ); + assert_eq!(assistant.messages.len(), 1); + assert_eq!(assistant.messages[0].excerpt_id, message_1.excerpt_id); assistant }); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index dca6f71797..a63f3404d3 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -579,7 +579,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) { assert_eq!(editor.scroll_manager.anchor(), original_scroll_position); // Ensure we don't panic when navigation data contains invalid anchors *and* points. - let mut invalid_anchor = editor.scroll_manager.anchor().top_anchor; + let mut invalid_anchor = editor.scroll_manager.anchor().anchor; invalid_anchor.text_anchor.buffer_id = Some(999); let invalid_point = Point::new(9999, 0); editor.navigate( @@ -587,7 +587,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) { cursor_anchor: invalid_anchor, cursor_position: invalid_point, scroll_anchor: ScrollAnchor { - top_anchor: invalid_anchor, + anchor: invalid_anchor, offset: Default::default(), }, scroll_top_row: invalid_point.row, @@ -5815,7 +5815,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) { let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0); follower.set_scroll_anchor( ScrollAnchor { - top_anchor, + anchor: top_anchor, offset: vec2f(0.0, 0.5), }, cx, diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 8da746075e..9d639f9b7b 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -196,7 +196,7 @@ impl FollowableItem for Editor { singleton: buffer.is_singleton(), title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()), excerpts, - scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.top_anchor)), + scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)), scroll_x: scroll_anchor.offset.x(), scroll_y: scroll_anchor.offset.y(), selections: self @@ -253,7 +253,7 @@ impl FollowableItem for Editor { } Event::ScrollPositionChanged { .. } => { let scroll_anchor = self.scroll_manager.anchor(); - update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.top_anchor)); + update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor)); update.scroll_x = scroll_anchor.offset.x(); update.scroll_y = scroll_anchor.offset.y(); true @@ -412,7 +412,7 @@ async fn update_editor_from_message( } else if let Some(scroll_top_anchor) = scroll_top_anchor { editor.set_scroll_anchor_remote( ScrollAnchor { - top_anchor: scroll_top_anchor, + anchor: scroll_top_anchor, offset: vec2f(message.scroll_x, message.scroll_y), }, cx, @@ -510,8 +510,8 @@ impl Item for Editor { }; let mut scroll_anchor = data.scroll_anchor; - if !buffer.can_resolve(&scroll_anchor.top_anchor) { - scroll_anchor.top_anchor = buffer.anchor_before( + if !buffer.can_resolve(&scroll_anchor.anchor) { + scroll_anchor.anchor = buffer.anchor_before( buffer.clip_point(Point::new(data.scroll_top_row, 0), Bias::Left), ); } diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index 21894dea88..17e8d18a62 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -36,21 +36,21 @@ pub struct ScrollbarAutoHide(pub bool); #[derive(Clone, Copy, Debug, PartialEq)] pub struct ScrollAnchor { pub offset: Vector2F, - pub top_anchor: Anchor, + pub anchor: Anchor, } impl ScrollAnchor { fn new() -> Self { Self { offset: Vector2F::zero(), - top_anchor: Anchor::min(), + anchor: Anchor::min(), } } pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F { let mut scroll_position = self.offset; - if self.top_anchor != Anchor::min() { - let scroll_top = self.top_anchor.to_display_point(snapshot).row() as f32; + if self.anchor != Anchor::min() { + let scroll_top = self.anchor.to_display_point(snapshot).row() as f32; scroll_position.set_y(scroll_top + scroll_position.y()); } else { scroll_position.set_y(0.); @@ -59,7 +59,7 @@ impl ScrollAnchor { } pub fn top_row(&self, buffer: &MultiBufferSnapshot) -> u32 { - self.top_anchor.to_point(buffer).row + self.anchor.to_point(buffer).row } } @@ -179,7 +179,7 @@ impl ScrollManager { let (new_anchor, top_row) = if scroll_position.y() <= 0. { ( ScrollAnchor { - top_anchor: Anchor::min(), + anchor: Anchor::min(), offset: scroll_position.max(vec2f(0., 0.)), }, 0, @@ -193,7 +193,7 @@ impl ScrollManager { ( ScrollAnchor { - top_anchor, + anchor: top_anchor, offset: vec2f( scroll_position.x(), scroll_position.y() - top_anchor.to_display_point(&map).row() as f32, @@ -322,7 +322,7 @@ impl Editor { hide_hover(self, cx); let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); let top_row = scroll_anchor - .top_anchor + .anchor .to_point(&self.buffer().read(cx).snapshot(cx)) .row; self.scroll_manager @@ -337,7 +337,7 @@ impl Editor { hide_hover(self, cx); let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); let top_row = scroll_anchor - .top_anchor + .anchor .to_point(&self.buffer().read(cx).snapshot(cx)) .row; self.scroll_manager @@ -377,7 +377,7 @@ impl Editor { let screen_top = self .scroll_manager .anchor - .top_anchor + .anchor .to_display_point(&snapshot); if screen_top > newest_head { @@ -408,7 +408,7 @@ impl Editor { .anchor_at(Point::new(top_row as u32, 0), Bias::Left); let scroll_anchor = ScrollAnchor { offset: Vector2F::new(x, y), - top_anchor, + anchor: top_anchor, }; self.set_scroll_anchor(scroll_anchor, cx); } diff --git a/crates/editor/src/scroll/actions.rs b/crates/editor/src/scroll/actions.rs index a79b0f2449..da5e3603e7 100644 --- a/crates/editor/src/scroll/actions.rs +++ b/crates/editor/src/scroll/actions.rs @@ -86,7 +86,7 @@ impl Editor { editor.set_scroll_anchor( ScrollAnchor { - top_anchor: new_anchor, + anchor: new_anchor, offset: Default::default(), }, cx, @@ -113,7 +113,7 @@ impl Editor { editor.set_scroll_anchor( ScrollAnchor { - top_anchor: new_anchor, + anchor: new_anchor, offset: Default::default(), }, cx, @@ -143,7 +143,7 @@ impl Editor { editor.set_scroll_anchor( ScrollAnchor { - top_anchor: new_anchor, + anchor: new_anchor, offset: Default::default(), }, cx, diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 91be1022a3..1f90d259d3 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -400,7 +400,7 @@ fn scroll(editor: &mut Editor, amount: &ScrollAmount, cx: &mut ViewContext Date: Wed, 7 Jun 2023 15:24:08 +0200 Subject: [PATCH 118/131] Fix assistant panel tests --- crates/ai/src/assistant.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1a6da539fa..77353e1ee4 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1365,16 +1365,18 @@ mod tests { cx.add_model(|cx| { let mut assistant = Assistant::new(Default::default(), registry, cx); - let message_1 = assistant.insert_message_after(ExcerptId::max(), Role::Assistant, cx); - let message_2 = assistant.insert_message_after(message_1.excerpt_id, Role::User, cx); - let message_3 = assistant.insert_message_after(message_1.excerpt_id, Role::User, cx); + let message_1 = assistant.messages[0].clone(); + let message_2 = assistant.insert_message_after(ExcerptId::max(), Role::Assistant, cx); + let message_3 = assistant.insert_message_after(message_2.excerpt_id, Role::User, cx); + let message_4 = assistant.insert_message_after(message_2.excerpt_id, Role::User, cx); assistant.remove_empty_messages( - HashSet::from_iter([message_2.excerpt_id, message_3.excerpt_id]), + HashSet::from_iter([message_3.excerpt_id, message_4.excerpt_id]), Default::default(), cx, ); - assert_eq!(assistant.messages.len(), 1); + assert_eq!(assistant.messages.len(), 2); assert_eq!(assistant.messages[0].excerpt_id, message_1.excerpt_id); + assert_eq!(assistant.messages[1].excerpt_id, message_2.excerpt_id); assistant }); } From d2b8501347148e58b85a95831ee5ef510a611e45 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Wed, 7 Jun 2023 16:08:48 +0100 Subject: [PATCH 119/131] feat: change themes to return ThemeConfig --- styles/src/themes/andromeda/andromeda.ts | 75 +++++----- .../src/themes/atelier/atelier-cave-dark.ts | 27 ++-- .../src/themes/atelier/atelier-cave-light.ts | 27 ++-- .../src/themes/atelier/atelier-dune-dark.ts | 27 ++-- .../src/themes/atelier/atelier-dune-light.ts | 27 ++-- .../themes/atelier/atelier-estuary-dark.ts | 27 ++-- .../themes/atelier/atelier-estuary-light.ts | 31 ++--- .../src/themes/atelier/atelier-forest-dark.ts | 27 ++-- .../themes/atelier/atelier-forest-light.ts | 27 ++-- .../src/themes/atelier/atelier-heath-dark.ts | 27 ++-- .../src/themes/atelier/atelier-heath-light.ts | 27 ++-- .../themes/atelier/atelier-lakeside-dark.ts | 27 ++-- .../themes/atelier/atelier-lakeside-light.ts | 27 ++-- .../themes/atelier/atelier-plateau-dark.ts | 27 ++-- .../themes/atelier/atelier-plateau-light.ts | 31 ++--- .../themes/atelier/atelier-savanna-dark.ts | 27 ++-- .../themes/atelier/atelier-savanna-light.ts | 27 ++-- .../themes/atelier/atelier-seaside-dark.ts | 27 ++-- .../themes/atelier/atelier-seaside-light.ts | 27 ++-- .../atelier/atelier-sulphurpool-dark.ts | 27 ++-- .../atelier/atelier-sulphurpool-light.ts | 27 ++-- styles/src/themes/atelier/common.ts | 17 +-- styles/src/themes/ayu/ayu-dark.ts | 24 ++-- styles/src/themes/ayu/ayu-light.ts | 24 ++-- styles/src/themes/ayu/ayu-mirage.ts | 24 ++-- styles/src/themes/ayu/common.ts | 19 +-- styles/src/themes/common/colorScheme.ts | 5 - styles/src/themes/common/syntax.ts | 16 ++- styles/src/themes/gruvbox/gruvbox-common.ts | 27 ++-- .../src/themes/gruvbox/gruvbox-dark-hard.ts | 7 +- .../src/themes/gruvbox/gruvbox-dark-soft.ts | 7 +- styles/src/themes/gruvbox/gruvbox-dark.ts | 7 +- .../src/themes/gruvbox/gruvbox-light-hard.ts | 7 +- .../src/themes/gruvbox/gruvbox-light-soft.ts | 7 +- styles/src/themes/gruvbox/gruvbox-light.ts | 7 +- styles/src/themes/one/one-dark.ts | 131 ++++++++---------- styles/src/themes/one/one-light.ts | 128 ++++++++--------- styles/src/themes/rose-pine/rose-pine-dawn.ts | 75 +++++----- styles/src/themes/rose-pine/rose-pine-moon.ts | 75 +++++----- styles/src/themes/rose-pine/rose-pine.ts | 71 +++++----- styles/src/themes/sandcastle/sandcastle.ts | 71 +++++----- styles/src/themes/solarized/solarized.ts | 44 +++--- styles/src/themes/summercamp/summercamp.ts | 75 +++++----- 43 files changed, 649 insertions(+), 842 deletions(-) diff --git a/styles/src/themes/andromeda/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts index c8a075e7cb..52c29bb2ec 100644 --- a/styles/src/themes/andromeda/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -1,44 +1,39 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Andromeda" - -const ramps = { - neutral: chroma - .scale([ - "#1E2025", - "#23262E", - "#292E38", - "#2E323C", - "#ACA8AE", - "#CBC9CF", - "#E1DDE4", - "#F7F7F8", - ]) - .domain([0, 0.15, 0.25, 0.35, 0.7, 0.8, 0.9, 1]), - red: colorRamp(chroma("#F92672")), - orange: colorRamp(chroma("#F39C12")), - yellow: colorRamp(chroma("#FFE66D")), - green: colorRamp(chroma("#96E072")), - cyan: colorRamp(chroma("#00E8C6")), - blue: colorRamp(chroma("#0CA793")), - violet: colorRamp(chroma("#8A3FA6")), - magenta: colorRamp(chroma("#C74DED")), -} - -export const meta: Meta = { - name, +export const dark: ThemeConfig = { + name: "Andromeda", author: "EliverLara", - license: { - SPDX: "MIT", - }, - url: "https://github.com/EliverLara/Andromeda", -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/EliverLara/Andromeda", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#1E2025", + "#23262E", + "#292E38", + "#2E323C", + "#ACA8AE", + "#CBC9CF", + "#E1DDE4", + "#F7F7F8", + ]) + .domain([0, 0.15, 0.25, 0.35, 0.7, 0.8, 0.9, 1]), + red: colorRamp(chroma("#F92672")), + orange: colorRamp(chroma("#F39C12")), + yellow: colorRamp(chroma("#FFE66D")), + green: colorRamp(chroma("#96E072")), + cyan: colorRamp(chroma("#00E8C6")), + blue: colorRamp(chroma("#0CA793")), + violet: colorRamp(chroma("#8A3FA6")), + magenta: colorRamp(chroma("#C74DED")), + }, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/atelier/atelier-cave-dark.ts b/styles/src/themes/atelier/atelier-cave-dark.ts index d6337de5bd..ebec67b4c2 100644 --- a/styles/src/themes/atelier/atelier-cave-dark.ts +++ b/styles/src/themes/atelier/atelier-cave-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Cave Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", - }, colors: { base00: "#19171c", base01: "#26232a", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Cave Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-cave-light.ts b/styles/src/themes/atelier/atelier-cave-light.ts index 8769cd0359..c1b7a05d47 100644 --- a/styles/src/themes/atelier/atelier-cave-light.ts +++ b/styles/src/themes/atelier/atelier-cave-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Cave Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", - }, colors: { base00: "#efecf4", base01: "#e2dfe7", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Cave Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-dune-dark.ts b/styles/src/themes/atelier/atelier-dune-dark.ts index 534c174bf1..c2ebc424e7 100644 --- a/styles/src/themes/atelier/atelier-dune-dark.ts +++ b/styles/src/themes/atelier/atelier-dune-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Dune Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/", - }, colors: { base00: "#20201d", base01: "#292824", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Dune Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-dune-light.ts b/styles/src/themes/atelier/atelier-dune-light.ts index 52bef3acfb..01cb1d67cb 100644 --- a/styles/src/themes/atelier/atelier-dune-light.ts +++ b/styles/src/themes/atelier/atelier-dune-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Dune Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/", - }, colors: { base00: "#fefbec", base01: "#e8e4cf", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Dune Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-estuary-dark.ts b/styles/src/themes/atelier/atelier-estuary-dark.ts index 550327df27..8e32c1f68f 100644 --- a/styles/src/themes/atelier/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier/atelier-estuary-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Estuary Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary/", - }, colors: { base00: "#22221b", base01: "#302f27", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Estuary Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-estuary-light.ts b/styles/src/themes/atelier/atelier-estuary-light.ts index 0a2b7ccda1..75fcb8e830 100644 --- a/styles/src/themes/atelier/atelier-estuary-light.ts +++ b/styles/src/themes/atelier/atelier-estuary-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Estuary Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary/", - }, colors: { base00: "#f4f3ec", base01: "#e7e6df", @@ -30,14 +24,17 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Estuary Light`, author: meta.author, appearance: ThemeAppearance.Light, - inputColor:{ + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,10 +56,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - override: { syntax },} - ) + override: { syntax }, + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-forest-dark.ts b/styles/src/themes/atelier/atelier-forest-dark.ts index 5040e05603..7ee7ae4ab1 100644 --- a/styles/src/themes/atelier/atelier-forest-dark.ts +++ b/styles/src/themes/atelier/atelier-forest-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Forest Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest/", - }, colors: { base00: "#1b1918", base01: "#2c2421", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Forest Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-forest-light.ts b/styles/src/themes/atelier/atelier-forest-light.ts index cc222e501f..17d3b63d88 100644 --- a/styles/src/themes/atelier/atelier-forest-light.ts +++ b/styles/src/themes/atelier/atelier-forest-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Forest Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest/", - }, colors: { base00: "#f1efee", base01: "#e6e2e0", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Forest Light`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-heath-dark.ts b/styles/src/themes/atelier/atelier-heath-dark.ts index 4caa5fb341..11751367a3 100644 --- a/styles/src/themes/atelier/atelier-heath-dark.ts +++ b/styles/src/themes/atelier/atelier-heath-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Heath Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath/", - }, colors: { base00: "#1b181b", base01: "#292329", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Heath Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-heath-light.ts b/styles/src/themes/atelier/atelier-heath-light.ts index dc6231957a..07f4a9b3cb 100644 --- a/styles/src/themes/atelier/atelier-heath-light.ts +++ b/styles/src/themes/atelier/atelier-heath-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Heath Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath/", - }, colors: { base00: "#f7f3f7", base01: "#d8cad8", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Heath Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-lakeside-dark.ts b/styles/src/themes/atelier/atelier-lakeside-dark.ts index 5a1cdfc3a1..b1c98ddfdf 100644 --- a/styles/src/themes/atelier/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier/atelier-lakeside-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Lakeside Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/", - }, colors: { base00: "#161b1d", base01: "#1f292e", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Lakeside Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-lakeside-light.ts b/styles/src/themes/atelier/atelier-lakeside-light.ts index 764891fb1a..d960444def 100644 --- a/styles/src/themes/atelier/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier/atelier-lakeside-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Lakeside Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/", - }, colors: { base00: "#ebf8ff", base01: "#c1e4f6", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Lakeside Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-plateau-dark.ts b/styles/src/themes/atelier/atelier-plateau-dark.ts index cc85940085..74693b24fd 100644 --- a/styles/src/themes/atelier/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier/atelier-plateau-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Plateau Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau/", - }, colors: { base00: "#1b1818", base01: "#292424", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Plateau Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-plateau-light.ts b/styles/src/themes/atelier/atelier-plateau-light.ts index 3c2b920ec3..dd3130cea0 100644 --- a/styles/src/themes/atelier/atelier-plateau-light.ts +++ b/styles/src/themes/atelier/atelier-plateau-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Plateau Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau/", - }, colors: { base00: "#f4ecec", base01: "#e7dfdf", @@ -30,14 +24,17 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Plateau Light`, author: meta.author, appearance: ThemeAppearance.Light, - inputColor:{ + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, + inputColor: { neutral: chroma.scale( [ colors.base00, @@ -59,10 +56,8 @@ const theme = (variant: Variant) => { violet: colorRamp(chroma(colors.base0E)), magenta: colorRamp(chroma(colors.base0F)), }, - override: { syntax },} - ) + override: { syntax }, + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-savanna-dark.ts b/styles/src/themes/atelier/atelier-savanna-dark.ts index eecfbeb7ca..c387ac5ae9 100644 --- a/styles/src/themes/atelier/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier/atelier-savanna-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Savanna Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", - }, colors: { base00: "#171c19", base01: "#232a25", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Savanna Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-savanna-light.ts b/styles/src/themes/atelier/atelier-savanna-light.ts index 62966116ff..64edd406a8 100644 --- a/styles/src/themes/atelier/atelier-savanna-light.ts +++ b/styles/src/themes/atelier/atelier-savanna-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Savanna Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", - }, colors: { base00: "#ecf4ee", base01: "#dfe7e2", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Savanna Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-seaside-dark.ts b/styles/src/themes/atelier/atelier-seaside-dark.ts index 44ce425326..dbccb96013 100644 --- a/styles/src/themes/atelier/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier/atelier-seaside-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Seaside Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/", - }, colors: { base00: "#131513", base01: "#242924", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Seaside Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-seaside-light.ts b/styles/src/themes/atelier/atelier-seaside-light.ts index 0f0d830d22..a9c034ed44 100644 --- a/styles/src/themes/atelier/atelier-seaside-light.ts +++ b/styles/src/themes/atelier/atelier-seaside-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Seaside Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/", - }, colors: { base00: "#f4fbf4", base01: "#cfe8cf", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Seaside Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts index 2ee6c1d944..edfc518b8e 100644 --- a/styles/src/themes/atelier/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Sulphurpool Dark`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/", - }, colors: { base00: "#202746", base01: "#293256", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Sulphurpool Dark`, author: meta.author, appearance: ThemeAppearance.Dark, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale([ colors.base00, @@ -58,9 +55,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/atelier-sulphurpool-light.ts b/styles/src/themes/atelier/atelier-sulphurpool-light.ts index a1af81e5d5..fbef6683bf 100644 --- a/styles/src/themes/atelier/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-light.ts @@ -1,13 +1,7 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" -import { metaCommon, name, buildSyntax, Variant } from "./common" +import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common" +import { meta, buildSyntax, Variant } from "./common" const variant: Variant = { - meta: { - name: `${name} Sulphurpool Light`, - ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/", - }, colors: { base00: "#f5f7ff", base01: "#dfe2f1", @@ -30,13 +24,16 @@ const variant: Variant = { const syntax = buildSyntax(variant) -const theme = (variant: Variant) => { - const { meta, colors } = variant +const getTheme = (variant: Variant): ThemeConfig => { + const { colors } = variant - return createColorScheme({ - name: meta.name, + return { + name: `${meta.name} Sulphurpool Light`, author: meta.author, appearance: ThemeAppearance.Light, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: { neutral: chroma.scale( [ @@ -60,9 +57,7 @@ const theme = (variant: Variant) => { magenta: colorRamp(chroma(colors.base0F)), }, override: { syntax }, - }) + } } -export const dark = theme(variant) - -export const meta: Meta = variant.meta +export const theme = getTheme(variant) diff --git a/styles/src/themes/atelier/common.ts b/styles/src/themes/atelier/common.ts index 065fc506c0..961ca64270 100644 --- a/styles/src/themes/atelier/common.ts +++ b/styles/src/themes/atelier/common.ts @@ -1,7 +1,6 @@ -import { License, Meta, ThemeSyntax } from "../common" +import { ThemeLicenseType, ThemeConfig, ThemeSyntax } from "../../common" export interface Variant { - meta: Meta colors: { base00: string base01: string @@ -22,14 +21,12 @@ export interface Variant { } } -export const metaCommon: { - author: string - license: License -} = { +export const meta: Partial = { + name: "Atelier", author: "Bram de Haan (http://atelierbramdehaan.nl)", - license: { - SPDX: "MIT", - }, + licenseType: ThemeLicenseType.MIT, + licenseUrl: + "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", } export const buildSyntax = (variant: Variant): ThemeSyntax => { @@ -57,5 +54,3 @@ export const buildSyntax = (variant: Variant): ThemeSyntax => { keyword: { color: colors.base0E }, } } - -export const name = "Atelier" diff --git a/styles/src/themes/ayu/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts index c5d69bbae5..7feddacd2b 100644 --- a/styles/src/themes/ayu/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,18 +1,16 @@ -import { createColorScheme, ThemeAppearance } from "../common" -import { ayu, meta as themeMeta, buildTheme } from "./common" - -export const meta = { - ...themeMeta, - name: `${themeMeta.name} Dark`, -} +import { ThemeAppearance, ThemeConfig } from "../../common" +import { ayu, meta, buildTheme } from "./common" const variant = ayu.dark -const theme = buildTheme(variant, false) +const { ramps, syntax } = buildTheme(variant, false) -export const dark = createColorScheme({ - name: meta.name, +export const theme: ThemeConfig = { + name: `${meta.name} Dark`, author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: theme.ramps, - override: { syntax: theme.syntax }, -}) + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, + inputColor: ramps, + override: { syntax }, +} diff --git a/styles/src/themes/ayu/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts index 50bc71b74e..bf02385247 100644 --- a/styles/src/themes/ayu/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,18 +1,16 @@ -import { createColorScheme, ThemeAppearance } from "../common" -import { ayu, meta as themeMeta, buildTheme } from "./common" - -export const meta = { - ...themeMeta, - name: `${themeMeta.name} Light`, -} +import { ThemeAppearance, ThemeConfig } from "../../common" +import { ayu, meta, buildTheme } from "./common" const variant = ayu.light -const theme = buildTheme(variant, true) +const { ramps, syntax } = buildTheme(variant, true) -export const light = createColorScheme({ - name: meta.name, +export const theme: ThemeConfig = { + name: `${meta.name} Light`, author: meta.author, appearance: ThemeAppearance.Light, - inputColor: theme.ramps, - override: { syntax: theme.syntax }, -}) + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, + inputColor: ramps, + override: { syntax }, +} diff --git a/styles/src/themes/ayu/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts index 71d02d2a75..d2a69e7ab6 100644 --- a/styles/src/themes/ayu/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,18 +1,16 @@ -import { createColorScheme, ThemeAppearance } from "../common" -import { ayu, meta as themeMeta, buildTheme } from "./common" - -export const meta = { - ...themeMeta, - name: `${themeMeta.name} Mirage`, -} +import { ThemeAppearance, ThemeConfig } from "../../common" +import { ayu, meta, buildTheme } from "./common" const variant = ayu.mirage -const theme = buildTheme(variant, false) +const { ramps, syntax } = buildTheme(variant, false) -export const dark = createColorScheme({ - name: meta.name, +export const theme: ThemeConfig = { + name: `${meta.name} Mirage`, author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: theme.ramps, - override: { syntax: theme.syntax }, -}) + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, + inputColor: ramps, + override: { syntax }, +} diff --git a/styles/src/themes/ayu/common.ts b/styles/src/themes/ayu/common.ts index e586e2d22d..26e21c1910 100644 --- a/styles/src/themes/ayu/common.ts +++ b/styles/src/themes/ayu/common.ts @@ -1,8 +1,11 @@ import { dark, light, mirage } from "ayu" -import { ThemeSyntax } from "../common/syntax" -import chroma from "chroma-js" -import { colorRamp } from "../common/ramps" -import { Meta } from "../common/colorScheme" +import { + chroma, + colorRamp, + ThemeLicenseType, + ThemeConfig, + ThemeSyntax, +} from "../../common" export const ayu = { dark, @@ -74,11 +77,9 @@ export const buildSyntax = (t: typeof dark): ThemeSyntax => { } } -export const meta: Meta = { +export const meta: Partial = { name: "Ayu", author: "dempfi", - license: { - SPDX: "MIT", - }, - url: "https://github.com/dempfi/ayu", + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/dempfi/ayu", } diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index c32411f23d..d4ef2ae013 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -25,11 +25,6 @@ export interface ColorScheme { syntax?: Partial } -export interface MetaAndLicense { - meta: Meta - licenseFile: string -} - export interface Meta { name: string author: string diff --git a/styles/src/themes/common/syntax.ts b/styles/src/themes/common/syntax.ts index 1645aaea87..258b845142 100644 --- a/styles/src/themes/common/syntax.ts +++ b/styles/src/themes/common/syntax.ts @@ -117,7 +117,7 @@ export interface Syntax { export type ThemeSyntax = Partial const defaultSyntaxHighlightStyle: Omit = { - weight: fontWeights.normal, + weight: "normal", underline: false, italic: false, } @@ -140,12 +140,14 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax { // Mix the neutral and blue colors to get a // predictive color distinct from any other color in the theme - const predictive = chroma.mix( - colorScheme.ramps.neutral(0.4).hex(), - colorScheme.ramps.blue(0.4).hex(), - 0.45, - "lch" - ).hex() + const predictive = chroma + .mix( + colorScheme.ramps.neutral(0.4).hex(), + colorScheme.ramps.blue(0.4).hex(), + 0.45, + "lch" + ) + .hex() const color = { primary: colorScheme.ramps.neutral(1).hex(), diff --git a/styles/src/themes/gruvbox/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts index bad189600c..f1c04a069c 100644 --- a/styles/src/themes/gruvbox/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -1,19 +1,17 @@ -import chroma from "chroma-js" import { - Meta, + chroma, colorRamp, - createColorScheme, - ThemeSyntax, ThemeAppearance, -} from "../common" + ThemeLicenseType, + ThemeConfig, + ThemeSyntax, +} from "../../common" -export const meta: Meta = { +const meta: Partial = { name: "Gruvbox", - license: { - SPDX: "MIT", // "MIT/X11" - }, author: "morhetz ", - url: "https://github.com/morhetz/gruvbox", + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/morhetz/gruvbox", } const color = { @@ -168,7 +166,7 @@ const variant: Variant[] = [ }, ] -const buildVariant = (variant: Variant) => { +const buildVariant = (variant: Variant): ThemeConfig => { const { colors } = variant const name = `Gruvbox ${variant.name}` @@ -245,13 +243,16 @@ const buildVariant = (variant: Variant) => { title: { color: colors.green }, } - return createColorScheme({ + return { name, author: meta.author, appearance: variant.appearance as ThemeAppearance, + licenseType: meta.licenseType, + licenseUrl: meta.licenseUrl, + licenseFile: `${__dirname}/LICENSE`, inputColor: ramps, override: { syntax }, - }) + } } // Variants diff --git a/styles/src/themes/gruvbox/gruvbox-dark-hard.ts b/styles/src/themes/gruvbox/gruvbox-dark-hard.ts index 3723de4901..4102671189 100644 --- a/styles/src/themes/gruvbox/gruvbox-dark-hard.ts +++ b/styles/src/themes/gruvbox/gruvbox-dark-hard.ts @@ -1,6 +1 @@ -import { darkHard as dark, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Dark Hard` - -export { dark, meta } +export { darkHard } from "./gruvbox-common" diff --git a/styles/src/themes/gruvbox/gruvbox-dark-soft.ts b/styles/src/themes/gruvbox/gruvbox-dark-soft.ts index 2887572ead..d550d63768 100644 --- a/styles/src/themes/gruvbox/gruvbox-dark-soft.ts +++ b/styles/src/themes/gruvbox/gruvbox-dark-soft.ts @@ -1,6 +1 @@ -import { darkSoft as dark, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Dark Soft` - -export { dark, meta } +export { darkSoft } from "./gruvbox-common" diff --git a/styles/src/themes/gruvbox/gruvbox-dark.ts b/styles/src/themes/gruvbox/gruvbox-dark.ts index cff7bd8bf9..05850028a4 100644 --- a/styles/src/themes/gruvbox/gruvbox-dark.ts +++ b/styles/src/themes/gruvbox/gruvbox-dark.ts @@ -1,6 +1 @@ -import { darkDefault as dark, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Dark` - -export { dark, meta } +export { darkDefault } from "./gruvbox-common" diff --git a/styles/src/themes/gruvbox/gruvbox-light-hard.ts b/styles/src/themes/gruvbox/gruvbox-light-hard.ts index cf998ce588..ec3cddec75 100644 --- a/styles/src/themes/gruvbox/gruvbox-light-hard.ts +++ b/styles/src/themes/gruvbox/gruvbox-light-hard.ts @@ -1,6 +1 @@ -import { lightHard as light, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Dark Soft` - -export { light, meta } +export { lightHard } from "./gruvbox-common" diff --git a/styles/src/themes/gruvbox/gruvbox-light-soft.ts b/styles/src/themes/gruvbox/gruvbox-light-soft.ts index 90ec82e965..0888e847ac 100644 --- a/styles/src/themes/gruvbox/gruvbox-light-soft.ts +++ b/styles/src/themes/gruvbox/gruvbox-light-soft.ts @@ -1,6 +1 @@ -import { lightSoft as light, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Light Soft` - -export { light, meta } +export { lightSoft } from "./gruvbox-common" diff --git a/styles/src/themes/gruvbox/gruvbox-light.ts b/styles/src/themes/gruvbox/gruvbox-light.ts index e8f355cd11..6f53ce5299 100644 --- a/styles/src/themes/gruvbox/gruvbox-light.ts +++ b/styles/src/themes/gruvbox/gruvbox-light.ts @@ -1,6 +1 @@ -import { lightDefault as light, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta } -meta.name = `${commonMeta.name} Light` - -export { light, meta } +export { lightDefault } from "./gruvbox-common" diff --git a/styles/src/themes/one/one-dark.ts b/styles/src/themes/one/one-dark.ts index fe0d1a053f..69a5bd5575 100644 --- a/styles/src/themes/one/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -1,14 +1,11 @@ -import chroma from "chroma-js" -import { fontWeights } from "../../common" import { - Meta, + chroma, + fontWeights, colorRamp, - createColorScheme, - ThemeSyntax, ThemeAppearance, -} from "../common" - -const name = "One Dark" + ThemeLicenseType, + ThemeConfig, +} from "../../common" const color = { white: "#ACB2BE", @@ -23,68 +20,60 @@ const color = { purple: "#B478CF", } -const ramps = { - neutral: chroma - .scale([ - "#282c34", - "#353b45", - "#3e4451", - "#545862", - "#565c64", - "#abb2bf", - "#b6bdca", - "#c8ccd4", - ]) - .domain([0.05, 0.22, 0.25, 0.45, 0.62, 0.8, 0.9, 1]), - red: colorRamp(chroma(color.red)), - orange: colorRamp(chroma(color.orange)), - yellow: colorRamp(chroma(color.yellow)), - green: colorRamp(chroma(color.green)), - cyan: colorRamp(chroma(color.teal)), - blue: colorRamp(chroma(color.blue)), - violet: colorRamp(chroma(color.purple)), - magenta: colorRamp(chroma("#be5046")), -} - -const syntax: ThemeSyntax = { - boolean: { color: color.orange }, - comment: { color: color.grey }, - enum: { color: color.red }, - "emphasis.strong": { color: color.orange }, - function: { color: color.blue }, - keyword: { color: color.purple }, - linkText: { color: color.blue, italic: false }, - linkUri: { color: color.teal }, - number: { color: color.orange }, - constant: { color: color.yellow }, - operator: { color: color.teal }, - primary: { color: color.white }, - property: { color: color.red }, - punctuation: { color: color.white }, - "punctuation.list_marker": { color: color.red }, - "punctuation.special": { color: color.darkRed }, - string: { color: color.green }, - title: { color: color.red, weight: fontWeights.normal }, - "text.literal": { color: color.green }, - type: { color: color.teal }, - "variable.special": { color: color.orange }, - variant: { color: color.blue }, - constructor: { color: color.blue }, -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "One Dark", author: "simurai", - license: { - SPDX: "MIT", - }, - url: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, - override: { syntax }, -}) + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#282c34", + "#353b45", + "#3e4451", + "#545862", + "#565c64", + "#abb2bf", + "#b6bdca", + "#c8ccd4", + ]) + .domain([0.05, 0.22, 0.25, 0.45, 0.62, 0.8, 0.9, 1]), + red: colorRamp(chroma(color.red)), + orange: colorRamp(chroma(color.orange)), + yellow: colorRamp(chroma(color.yellow)), + green: colorRamp(chroma(color.green)), + cyan: colorRamp(chroma(color.teal)), + blue: colorRamp(chroma(color.blue)), + violet: colorRamp(chroma(color.purple)), + magenta: colorRamp(chroma("#be5046")), + }, + override: { + syntax: { + boolean: { color: color.orange }, + comment: { color: color.grey }, + enum: { color: color.red }, + "emphasis.strong": { color: color.orange }, + function: { color: color.blue }, + keyword: { color: color.purple }, + linkText: { color: color.blue, italic: false }, + linkUri: { color: color.teal }, + number: { color: color.orange }, + constant: { color: color.yellow }, + operator: { color: color.teal }, + primary: { color: color.white }, + property: { color: color.red }, + punctuation: { color: color.white }, + "punctuation.list_marker": { color: color.red }, + "punctuation.special": { color: color.darkRed }, + string: { color: color.green }, + title: { color: color.red, weight: fontWeights.normal }, + "text.literal": { color: color.green }, + type: { color: color.teal }, + "variable.special": { color: color.orange }, + variant: { color: color.blue }, + constructor: { color: color.blue }, + }, + }, +} diff --git a/styles/src/themes/one/one-light.ts b/styles/src/themes/one/one-light.ts index ac9876ecb4..9123c8879d 100644 --- a/styles/src/themes/one/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -1,14 +1,11 @@ -import chroma from "chroma-js" -import { fontWeights } from "../../common" import { - Meta, + chroma, + fontWeights, colorRamp, - createColorScheme, - ThemeSyntax, ThemeAppearance, -} from "../common" - -const name = "One Light" + ThemeLicenseType, + ThemeConfig, +} from "../../common" const color = { black: "#383A41", @@ -24,66 +21,59 @@ const color = { magenta: "#994EA6", } -const ramps = { - neutral: chroma - .scale([ - "#383A41", - "#535456", - "#696c77", - "#9D9D9F", - "#A9A9A9", - "#DBDBDC", - "#EAEAEB", - "#FAFAFA", - ]) - .domain([0.05, 0.22, 0.25, 0.45, 0.62, 0.8, 0.9, 1]), - red: colorRamp(chroma(color.red)), - orange: colorRamp(chroma(color.orange)), - yellow: colorRamp(chroma(color.yellow)), - green: colorRamp(chroma(color.green)), - cyan: colorRamp(chroma(color.teal)), - blue: colorRamp(chroma(color.blue)), - violet: colorRamp(chroma(color.purple)), - magenta: colorRamp(chroma(color.magenta)), -} - -const syntax: ThemeSyntax = { - boolean: { color: color.orange }, - comment: { color: color.grey }, - enum: { color: color.red }, - "emphasis.strong": { color: color.orange }, - function: { color: color.blue }, - keyword: { color: color.purple }, - linkText: { color: color.blue }, - linkUri: { color: color.teal }, - number: { color: color.orange }, - operator: { color: color.teal }, - primary: { color: color.black }, - property: { color: color.red }, - punctuation: { color: color.black }, - "punctuation.list_marker": { color: color.red }, - "punctuation.special": { color: color.darkRed }, - string: { color: color.green }, - title: { color: color.red, weight: fontWeights.normal }, - "text.literal": { color: color.green }, - type: { color: color.teal }, - "variable.special": { color: color.orange }, - variant: { color: color.blue }, -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "One Light", author: "simurai", - license: { - SPDX: "MIT", - }, - url: "https://github.com/atom/atom/tree/master/packages/one-light-ui", -} - -export const light = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Light, - inputColor: ramps, - override: { syntax }, -}) + licenseType: ThemeLicenseType.MIT, + licenseUrl: + "https://github.com/atom/atom/tree/master/packages/one-light-ui", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#383A41", + "#535456", + "#696c77", + "#9D9D9F", + "#A9A9A9", + "#DBDBDC", + "#EAEAEB", + "#FAFAFA", + ]) + .domain([0.05, 0.22, 0.25, 0.45, 0.62, 0.8, 0.9, 1]), + red: colorRamp(chroma(color.red)), + orange: colorRamp(chroma(color.orange)), + yellow: colorRamp(chroma(color.yellow)), + green: colorRamp(chroma(color.green)), + cyan: colorRamp(chroma(color.teal)), + blue: colorRamp(chroma(color.blue)), + violet: colorRamp(chroma(color.purple)), + magenta: colorRamp(chroma(color.magenta)), + }, + override: { + syntax: { + boolean: { color: color.orange }, + comment: { color: color.grey }, + enum: { color: color.red }, + "emphasis.strong": { color: color.orange }, + function: { color: color.blue }, + keyword: { color: color.purple }, + linkText: { color: color.blue }, + linkUri: { color: color.teal }, + number: { color: color.orange }, + operator: { color: color.teal }, + primary: { color: color.black }, + property: { color: color.red }, + punctuation: { color: color.black }, + "punctuation.list_marker": { color: color.red }, + "punctuation.special": { color: color.darkRed }, + string: { color: color.green }, + title: { color: color.red, weight: fontWeights.normal }, + "text.literal": { color: color.green }, + type: { color: color.teal }, + "variable.special": { color: color.orange }, + variant: { color: color.blue }, + }, + }, +} diff --git a/styles/src/themes/rose-pine/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts index e9296a67ee..a373ed378c 100644 --- a/styles/src/themes/rose-pine/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -1,44 +1,39 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Rosé Pine Dawn" - -const ramps = { - neutral: chroma - .scale([ - "#575279", - "#797593", - "#9893A5", - "#B5AFB8", - "#D3CCCC", - "#F2E9E1", - "#FFFAF3", - "#FAF4ED", - ]) - .domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]), - red: colorRamp(chroma("#B4637A")), - orange: colorRamp(chroma("#D7827E")), - yellow: colorRamp(chroma("#EA9D34")), - green: colorRamp(chroma("#679967")), - cyan: colorRamp(chroma("#286983")), - blue: colorRamp(chroma("#56949F")), - violet: colorRamp(chroma("#907AA9")), - magenta: colorRamp(chroma("#79549F")), -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "Rosé Pine Dawn", author: "edunfelt", - license: { - SPDX: "MIT", - }, - url: "https://github.com/edunfelt/base16-rose-pine-scheme", -} - -export const light = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Light, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#575279", + "#797593", + "#9893A5", + "#B5AFB8", + "#D3CCCC", + "#F2E9E1", + "#FFFAF3", + "#FAF4ED", + ]) + .domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]), + red: colorRamp(chroma("#B4637A")), + orange: colorRamp(chroma("#D7827E")), + yellow: colorRamp(chroma("#EA9D34")), + green: colorRamp(chroma("#679967")), + cyan: colorRamp(chroma("#286983")), + blue: colorRamp(chroma("#56949F")), + violet: colorRamp(chroma("#907AA9")), + magenta: colorRamp(chroma("#79549F")), + }, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/rose-pine/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts index deef789960..94b8166cb3 100644 --- a/styles/src/themes/rose-pine/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -1,44 +1,39 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Rosé Pine Moon" - -const ramps = { - neutral: chroma - .scale([ - "#232136", - "#2A273F", - "#393552", - "#3E3A53", - "#56526C", - "#6E6A86", - "#908CAA", - "#E0DEF4", - ]) - .domain([0, 0.3, 0.55, 1]), - red: colorRamp(chroma("#EB6F92")), - orange: colorRamp(chroma("#EBBCBA")), - yellow: colorRamp(chroma("#F6C177")), - green: colorRamp(chroma("#8DBD8D")), - cyan: colorRamp(chroma("#409BBE")), - blue: colorRamp(chroma("#9CCFD8")), - violet: colorRamp(chroma("#C4A7E7")), - magenta: colorRamp(chroma("#AB6FE9")), -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "Rosé Pine Moon", author: "edunfelt", - license: { - SPDX: "MIT", - }, - url: "https://github.com/edunfelt/base16-rose-pine-scheme", -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#232136", + "#2A273F", + "#393552", + "#3E3A53", + "#56526C", + "#6E6A86", + "#908CAA", + "#E0DEF4", + ]) + .domain([0, 0.3, 0.55, 1]), + red: colorRamp(chroma("#EB6F92")), + orange: colorRamp(chroma("#EBBCBA")), + yellow: colorRamp(chroma("#F6C177")), + green: colorRamp(chroma("#8DBD8D")), + cyan: colorRamp(chroma("#409BBE")), + blue: colorRamp(chroma("#9CCFD8")), + violet: colorRamp(chroma("#C4A7E7")), + magenta: colorRamp(chroma("#AB6FE9")), + }, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/rose-pine/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts index 233d8225f3..3aabe3f10e 100644 --- a/styles/src/themes/rose-pine/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -1,42 +1,37 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Rosé Pine" - -const ramps = { - neutral: chroma.scale([ - "#191724", - "#1f1d2e", - "#26233A", - "#3E3A53", - "#56526C", - "#6E6A86", - "#908CAA", - "#E0DEF4", - ]), - red: colorRamp(chroma("#EB6F92")), - orange: colorRamp(chroma("#EBBCBA")), - yellow: colorRamp(chroma("#F6C177")), - green: colorRamp(chroma("#8DBD8D")), - cyan: colorRamp(chroma("#409BBE")), - blue: colorRamp(chroma("#9CCFD8")), - violet: colorRamp(chroma("#C4A7E7")), - magenta: colorRamp(chroma("#AB6FE9")), -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "Rosé Pine", author: "edunfelt", - license: { - SPDX: "MIT", - }, - url: "https://github.com/edunfelt/base16-rose-pine-scheme", -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma.scale([ + "#191724", + "#1f1d2e", + "#26233A", + "#3E3A53", + "#56526C", + "#6E6A86", + "#908CAA", + "#E0DEF4", + ]), + red: colorRamp(chroma("#EB6F92")), + orange: colorRamp(chroma("#EBBCBA")), + yellow: colorRamp(chroma("#F6C177")), + green: colorRamp(chroma("#8DBD8D")), + cyan: colorRamp(chroma("#409BBE")), + blue: colorRamp(chroma("#9CCFD8")), + violet: colorRamp(chroma("#C4A7E7")), + magenta: colorRamp(chroma("#AB6FE9")), + }, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/sandcastle/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts index ab36d725a9..753828c665 100644 --- a/styles/src/themes/sandcastle/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -1,42 +1,37 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Sandcastle" - -const ramps = { - neutral: chroma.scale([ - "#282c34", - "#2c323b", - "#3e4451", - "#665c54", - "#928374", - "#a89984", - "#d5c4a1", - "#fdf4c1", - ]), - red: colorRamp(chroma("#B4637A")), - orange: colorRamp(chroma("#a07e3b")), - yellow: colorRamp(chroma("#a07e3b")), - green: colorRamp(chroma("#83a598")), - cyan: colorRamp(chroma("#83a598")), - blue: colorRamp(chroma("#528b8b")), - violet: colorRamp(chroma("#d75f5f")), - magenta: colorRamp(chroma("#a87322")), -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "Sandcastle", author: "gessig", - license: { - SPDX: "MIT", - }, - url: "https://github.com/gessig/base16-sandcastle-scheme", -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/gessig/base16-sandcastle-scheme", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma.scale([ + "#282c34", + "#2c323b", + "#3e4451", + "#665c54", + "#928374", + "#a89984", + "#d5c4a1", + "#fdf4c1", + ]), + red: colorRamp(chroma("#B4637A")), + orange: colorRamp(chroma("#a07e3b")), + yellow: colorRamp(chroma("#a07e3b")), + green: colorRamp(chroma("#83a598")), + cyan: colorRamp(chroma("#83a598")), + blue: colorRamp(chroma("#528b8b")), + violet: colorRamp(chroma("#d75f5f")), + magenta: colorRamp(chroma("#a87322")), + }, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/solarized/solarized.ts b/styles/src/themes/solarized/solarized.ts index e4c86d2939..4084757525 100644 --- a/styles/src/themes/solarized/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -1,7 +1,10 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" - -const name = "Solarized" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" const ramps = { neutral: chroma @@ -26,27 +29,24 @@ const ramps = { magenta: colorRamp(chroma("#d33682")), } -export const meta: Meta = { - name, +export const dark: ThemeConfig = { + name: "Solarized Dark", author: "Ethan Schoonover", - license: { - SPDX: "MIT", - }, - url: "https://github.com/altercation/solarized", + appearance: ThemeAppearance.Dark, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/altercation/solarized", + licenseFile: `${__dirname}/LICENSE`, + inputColor: ramps, + override: { syntax: {} }, } -export const dark = createColorScheme({ - name: `${name} Dark`, - author: meta.author, - appearance: ThemeAppearance.Dark, - inputColor: ramps, - override: { syntax: {} }, -}) - -export const light = createColorScheme({ - name: `${name} Light`, - author: meta.author, +export const light: ThemeConfig = { + name: "Solarized Light", + author: "Ethan Schoonover", appearance: ThemeAppearance.Light, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/altercation/solarized", + licenseFile: `${__dirname}/LICENSE`, inputColor: ramps, override: { syntax: {} }, -}) +} diff --git a/styles/src/themes/summercamp/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts index f9df22a224..08098d2e2f 100644 --- a/styles/src/themes/summercamp/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -1,44 +1,39 @@ -import chroma from "chroma-js" -import { Meta, colorRamp, createColorScheme, ThemeAppearance } from "../common" +import { + chroma, + colorRamp, + ThemeAppearance, + ThemeLicenseType, + ThemeConfig, +} from "../../common" -const name = "Summercamp" - -const ramps = { - neutral: chroma - .scale([ - "#1c1810", - "#2a261c", - "#3a3527", - "#3a3527", - "#5f5b45", - "#736e55", - "#bab696", - "#f8f5de", - ]) - .domain([0, 0.2, 0.38, 0.4, 0.65, 0.7, 0.85, 1]), - red: colorRamp(chroma("#e35142")), - orange: colorRamp(chroma("#fba11b")), - yellow: colorRamp(chroma("#f2ff27")), - green: colorRamp(chroma("#5ceb5a")), - cyan: colorRamp(chroma("#5aebbc")), - blue: colorRamp(chroma("#489bf0")), - violet: colorRamp(chroma("#FF8080")), - magenta: colorRamp(chroma("#F69BE7")), -} - -export const meta: Meta = { - name, +export const theme: ThemeConfig = { + name: "Summercamp", author: "zoefiri", - url: "https://github.com/zoefiri/base16-sc", - license: { - SPDX: "MIT", - }, -} - -export const dark = createColorScheme({ - name: meta.name, - author: meta.author, appearance: ThemeAppearance.Dark, - inputColor: ramps, + licenseType: ThemeLicenseType.MIT, + licenseUrl: "https://github.com/zoefiri/base16-sc", + licenseFile: `${__dirname}/LICENSE`, + inputColor: { + neutral: chroma + .scale([ + "#1c1810", + "#2a261c", + "#3a3527", + "#3a3527", + "#5f5b45", + "#736e55", + "#bab696", + "#f8f5de", + ]) + .domain([0, 0.2, 0.38, 0.4, 0.65, 0.7, 0.85, 1]), + red: colorRamp(chroma("#e35142")), + orange: colorRamp(chroma("#fba11b")), + yellow: colorRamp(chroma("#f2ff27")), + green: colorRamp(chroma("#5ceb5a")), + cyan: colorRamp(chroma("#5aebbc")), + blue: colorRamp(chroma("#489bf0")), + violet: colorRamp(chroma("#FF8080")), + magenta: colorRamp(chroma("#F69BE7")), + }, override: { syntax: {} }, -}) +} From f5d1f314e07059c00daeda2f03fa0ea6e16c3312 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Wed, 7 Jun 2023 16:08:58 +0100 Subject: [PATCH 120/131] feat: add themes index --- styles/src/themes/index.ts | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 styles/src/themes/index.ts diff --git a/styles/src/themes/index.ts b/styles/src/themes/index.ts new file mode 100644 index 0000000000..52ce1b9327 --- /dev/null +++ b/styles/src/themes/index.ts @@ -0,0 +1,82 @@ +import { ThemeConfig } from "./common" +import { darkDefault as gruvboxDark } from "./gruvbox/gruvbox-dark" +import { darkHard as gruvboxDarkHard } from "./gruvbox/gruvbox-dark-hard" +import { darkSoft as gruvboxDarkSoft } from "./gruvbox/gruvbox-dark-soft" +import { lightDefault as gruvboxLight } from "./gruvbox/gruvbox-light" +import { lightHard as gruvboxLightHard } from "./gruvbox/gruvbox-light-hard" +import { lightSoft as gruvboxLightSoft } from "./gruvbox/gruvbox-light-soft" +import { dark as solarizedDark } from "./solarized/solarized" +import { light as solarizedLight } from "./solarized/solarized" +import { dark as andromedaDark } from "./andromeda/andromeda" +import { theme as oneDark } from "./one/one-dark" +import { theme as oneLight } from "./one/one-light" +import { theme as ayuLight } from "./ayu/ayu-light" +import { theme as ayuDark } from "./ayu/ayu-dark" +import { theme as ayuMirage } from "./ayu/ayu-mirage" +import { theme as rosePine } from "./rose-pine/rose-pine" +import { theme as rosePineDawn } from "./rose-pine/rose-pine-dawn" +import { theme as rosePineMoon } from "./rose-pine/rose-pine-moon" +import { theme as sandcastle } from "./sandcastle/sandcastle" +import { theme as summercamp } from "./summercamp/summercamp" +import { theme as atelierCaveDark } from "./atelier/atelier-cave-dark" +import { theme as atelierCaveLight } from "./atelier/atelier-cave-light" +import { theme as atelierDuneDark } from "./atelier/atelier-dune-dark" +import { theme as atelierDuneLight } from "./atelier/atelier-dune-light" +import { theme as atelierEstuaryDark } from "./atelier/atelier-estuary-dark" +import { theme as atelierEstuaryLight } from "./atelier/atelier-estuary-light" +import { theme as atelierForestDark } from "./atelier/atelier-forest-dark" +import { theme as atelierForestLight } from "./atelier/atelier-forest-light" +import { theme as atelierHeathDark } from "./atelier/atelier-heath-dark" +import { theme as atelierHeathLight } from "./atelier/atelier-heath-light" +import { theme as atelierLakesideDark } from "./atelier/atelier-lakeside-dark" +import { theme as atelierLakesideLight } from "./atelier/atelier-lakeside-light" +import { theme as atelierPlateauDark } from "./atelier/atelier-plateau-dark" +import { theme as atelierPlateauLight } from "./atelier/atelier-plateau-light" +import { theme as atelierSavannaDark } from "./atelier/atelier-savanna-dark" +import { theme as atelierSavannaLight } from "./atelier/atelier-savanna-light" +import { theme as atelierSeasideDark } from "./atelier/atelier-seaside-dark" +import { theme as atelierSeasideLight } from "./atelier/atelier-seaside-light" +import { theme as atelierSulphurpoolDark } from "./atelier/atelier-sulphurpool-dark" +import { theme as atelierSulphurpoolLight } from "./atelier/atelier-sulphurpool-light" + +export const themes: ThemeConfig[] = [ + oneDark, + oneLight, + ayuLight, + ayuDark, + ayuMirage, + gruvboxDark, + gruvboxDarkHard, + gruvboxDarkSoft, + gruvboxLight, + gruvboxLightHard, + gruvboxLightSoft, + rosePine, + rosePineDawn, + rosePineMoon, + sandcastle, + solarizedDark, + solarizedLight, + andromedaDark, + summercamp, + atelierCaveDark, + atelierCaveLight, + atelierDuneDark, + atelierDuneLight, + atelierEstuaryDark, + atelierEstuaryLight, + atelierForestDark, + atelierForestLight, + atelierHeathDark, + atelierHeathLight, + atelierLakesideDark, + atelierLakesideLight, + atelierPlateauDark, + atelierPlateauLight, + atelierSavannaDark, + atelierSavannaLight, + atelierSeasideDark, + atelierSeasideLight, + atelierSulphurpoolDark, + atelierSulphurpoolLight, +] From 02c1efc60d572109d928f9f59dbe2751a23b5971 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Wed, 7 Jun 2023 16:09:24 +0100 Subject: [PATCH 121/131] feat: re-export chroma --- styles/src/common.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/styles/src/common.ts b/styles/src/common.ts index 1b4fb6b37b..5b6ddff422 100644 --- a/styles/src/common.ts +++ b/styles/src/common.ts @@ -1,3 +1,7 @@ +import chroma from "chroma-js" +export * from "./themes/common" +export { chroma } + export const fontFamilies = { sans: "Zed Sans", mono: "Zed Mono", @@ -23,6 +27,7 @@ export type FontWeight = | "bold" | "extra_bold" | "black" + export const fontWeights: { [key: string]: FontWeight } = { thin: "thin", extra_light: "extra_light", From 0ad76ac92c06aca369964bc827256b0cc0516ecf Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Wed, 7 Jun 2023 16:10:02 +0100 Subject: [PATCH 122/131] feat: use theme index to build themes --- styles/src/buildThemes.ts | 30 ++++++--------- styles/src/colorSchemes.ts | 79 -------------------------------------- styles/src/themeConfig.ts | 8 +++- 3 files changed, 19 insertions(+), 98 deletions(-) delete mode 100644 styles/src/colorSchemes.ts diff --git a/styles/src/buildThemes.ts b/styles/src/buildThemes.ts index 4353765573..7173f1cb0a 100644 --- a/styles/src/buildThemes.ts +++ b/styles/src/buildThemes.ts @@ -1,17 +1,21 @@ import * as fs from "fs" import { tmpdir } from "os" import * as path from "path" -import { colorSchemes, staffColorSchemes } from "./colorSchemes" import app from "./styleTree/app" -import { ColorScheme } from "./themes/common/colorScheme" +import { ColorScheme, createColorScheme } from "./themes/common/colorScheme" import snakeCase from "./utils/snakeCase" +import { themes } from "./themes" const assetsDirectory = `${__dirname}/../../assets` -const themeDirectory = `${assetsDirectory}/themes` -const staffDirectory = `${themeDirectory}/staff` - const tempDirectory = fs.mkdtempSync(path.join(tmpdir(), "build-themes")) +function getColorSchemes() { + const colorSchemes: ColorScheme[] = themes.map((theme) => + createColorScheme(theme) + ) + return colorSchemes +} + // Clear existing themes function clearThemes(themeDirectory: string) { if (!fs.existsSync(themeDirectory)) { @@ -19,23 +23,14 @@ function clearThemes(themeDirectory: string) { } else { for (const file of fs.readdirSync(themeDirectory)) { if (file.endsWith(".json")) { - const name = file.replace(/\.json$/, "") - if ( - !colorSchemes.find( - (colorScheme) => colorScheme.name === name - ) - ) { - fs.unlinkSync(path.join(themeDirectory, file)) - } + fs.unlinkSync(path.join(themeDirectory, file)) } } } } -clearThemes(themeDirectory) -clearThemes(staffDirectory) - function writeThemes(colorSchemes: ColorScheme[], outputDirectory: string) { + clearThemes(outputDirectory) for (let colorScheme of colorSchemes) { let styleTree = snakeCase(app(colorScheme)) let styleTreeJSON = JSON.stringify(styleTree, null, 2) @@ -48,5 +43,4 @@ function writeThemes(colorSchemes: ColorScheme[], outputDirectory: string) { } // Write new themes to theme directory -writeThemes(colorSchemes, themeDirectory) -writeThemes(staffColorSchemes, staffDirectory) +writeThemes(getColorSchemes(), `${assetsDirectory}/themes`) diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts deleted file mode 100644 index 74f1a2a03c..0000000000 --- a/styles/src/colorSchemes.ts +++ /dev/null @@ -1,79 +0,0 @@ -import fs from "fs" -import path from "path" -import { ColorScheme, MetaAndLicense } from "./themes/common/colorScheme" - -const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) -const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) -const IGNORE_ITEMS = ["staff", "common", "common.ts"] -const ACCEPT_EXTENSION = ".ts" -const LICENSE_FILE_NAME = "LICENSE" - -function getAllTsFiles(directoryPath: string) { - const files = fs.readdirSync(directoryPath) - const fileList: string[] = [] - - for (const file of files) { - if (!IGNORE_ITEMS.includes(file)) { - const filePath = path.join(directoryPath, file) - - if (fs.statSync(filePath).isDirectory()) { - fileList.push(...getAllTsFiles(filePath)) - } else if (path.extname(file) === ACCEPT_EXTENSION) { - fileList.push(filePath) - } - } - } - - return fileList -} - -function getAllColorSchemes(directoryPath: string) { - const files = getAllTsFiles(directoryPath) - return files.map((filePath) => ({ - colorScheme: require(filePath), - filePath, - fileName: path.basename(filePath), - licenseFile: `${path.dirname(filePath)}/${LICENSE_FILE_NAME}`, - })) -} - -function getColorSchemes(directoryPath: string) { - const colorSchemes: ColorScheme[] = [] - - for (const { colorScheme } of getAllColorSchemes(directoryPath)) { - if (colorScheme.dark) colorSchemes.push(colorScheme.dark) - else if (colorScheme.light) colorSchemes.push(colorScheme.light) - } - - return colorSchemes -} - -function getMetaAndLicense(directoryPath: string) { - const meta: MetaAndLicense[] = [] - - for (const { colorScheme, filePath, licenseFile } of getAllColorSchemes( - directoryPath - )) { - const licenseExists = fs.existsSync(licenseFile) - if (!licenseExists) { - throw Error( - `Public theme should have a LICENSE file ${licenseFile}` - ) - } - - if (!colorScheme.meta) { - throw Error(`Public theme ${filePath} must have a meta field`) - } - - meta.push({ - meta: colorScheme.meta, - licenseFile, - }) - } - - return meta -} - -export const colorSchemes = getColorSchemes(THEMES_DIRECTORY) -export const staffColorSchemes = getColorSchemes(STAFF_DIRECTORY) -export const schemeMeta = getMetaAndLicense(THEMES_DIRECTORY) diff --git a/styles/src/themeConfig.ts b/styles/src/themeConfig.ts index 0b93a67853..5c62b94c52 100644 --- a/styles/src/themeConfig.ts +++ b/styles/src/themeConfig.ts @@ -17,8 +17,9 @@ interface ThemeMeta { * * Example: `MIT` */ - licenseType?: string + licenseType?: string | ThemeLicenseType licenseUrl?: string + licenseFile: string themeUrl?: string } @@ -94,6 +95,11 @@ export enum ThemeAppearance { Dark = "dark", } +export enum ThemeLicenseType { + MIT = "MIT", + Apache2 = "Apache License 2.0", +} + export type ThemeFamilyItem = | ThemeConfig | { light: ThemeConfig; dark: ThemeConfig } From 4c405e65a328714c424d053b54caa68ae91e79a2 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Wed, 7 Jun 2023 16:10:16 +0100 Subject: [PATCH 123/131] feat: use theme index to build licenses --- styles/src/buildLicenses.ts | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 9a053e9c0c..9827583aeb 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -1,7 +1,7 @@ import * as fs from "fs" import toml from "toml" -import { schemeMeta } from "./colorSchemes" -import { MetaAndLicense } from "./themes/common/colorScheme" +import { themes } from "./themes" +import { ThemeConfig } from "./common" const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml` @@ -18,37 +18,31 @@ function parseAcceptedToml(file: string): string[] { return obj.accepted } -function checkLicenses( - schemeMetaWithLicense: MetaAndLicense[], - licenses: string[] -) { - for (const { meta } of schemeMetaWithLicense) { - // FIXME: Add support for conjuctions and conditions - if (licenses.indexOf(meta.license.SPDX) < 0) { - throw Error( - `License for theme ${meta.name} (${meta.license.SPDX}) is not supported` - ) +function checkLicenses(themes: ThemeConfig[]) { + for (const theme of themes) { + if (!theme.licenseFile) { + throw Error(`Theme ${theme.name} should have a LICENSE files`) } } } -function generateLicenseFile(schemeMetaWithLicense: MetaAndLicense[]) { - for (const { meta, licenseFile } of schemeMetaWithLicense) { - const licenseText = fs.readFileSync(licenseFile).toString() - writeLicense(meta.name, meta.url, licenseText) +function generateLicenseFile(themes: ThemeConfig[]) { + checkLicenses(themes) + for (const theme of themes) { + const licenseText = fs.readFileSync(theme.licenseFile).toString() + writeLicense(theme.name, theme.licenseUrl, licenseText) } } function writeLicense( themeName: string, - themeUrl: string, + licenseUrl: string, licenseText: String ) { process.stdout.write( - `## [${themeName}](${themeUrl})\n\n${licenseText}\n********************************************************************************\n\n` + `## [${themeName}](${licenseUrl})\n\n${licenseText}\n********************************************************************************\n\n` ) } const acceptedLicenses = parseAcceptedToml(ACCEPTED_LICENSES_FILE) -checkLicenses(schemeMeta, acceptedLicenses) -generateLicenseFile(schemeMeta) +generateLicenseFile(themes) From e94129446d9748a7d26c8a265ca5ca6d74375201 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 7 Jun 2023 17:37:02 +0200 Subject: [PATCH 124/131] Z 1618/extend comments (#2585) Fixes Z-1618. In the current state, this only works for line comments such as `//` (and whatever's set in `{language}.toml` as a line_comment). Release Notes: - Comments are now extended on new line. --- crates/editor/src/editor.rs | 90 +++++++++++++++++++++---------- crates/editor/src/editor_tests.rs | 27 ++++++++++ 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a1e354a4bc..708a22c5c6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2179,40 +2179,72 @@ impl Editor { indent.len = cmp::min(indent.len, start_point.column); let start = selection.start; let end = selection.end; + let is_cursor = start == end; + let language_scope = buffer.language_scope_at(start); + let (comment_delimiter, insert_extra_newline) = + if let Some(language) = &language_scope { + let leading_whitespace_len = buffer + .reversed_chars_at(start) + .take_while(|c| c.is_whitespace() && *c != '\n') + .map(|c| c.len_utf8()) + .sum::(); - let mut insert_extra_newline = false; - if let Some(language) = buffer.language_scope_at(start) { - let leading_whitespace_len = buffer - .reversed_chars_at(start) - .take_while(|c| c.is_whitespace() && *c != '\n') - .map(|c| c.len_utf8()) - .sum::(); + let trailing_whitespace_len = buffer + .chars_at(end) + .take_while(|c| c.is_whitespace() && *c != '\n') + .map(|c| c.len_utf8()) + .sum::(); - let trailing_whitespace_len = buffer - .chars_at(end) - .take_while(|c| c.is_whitespace() && *c != '\n') - .map(|c| c.len_utf8()) - .sum::(); + let insert_extra_newline = + language.brackets().any(|(pair, enabled)| { + let pair_start = pair.start.trim_end(); + let pair_end = pair.end.trim_start(); - insert_extra_newline = language.brackets().any(|(pair, enabled)| { - let pair_start = pair.start.trim_end(); - let pair_end = pair.end.trim_start(); + enabled + && pair.newline + && buffer.contains_str_at( + end + trailing_whitespace_len, + pair_end, + ) + && buffer.contains_str_at( + (start - leading_whitespace_len) + .saturating_sub(pair_start.len()), + pair_start, + ) + }); + // Comment extension on newline is allowed only for cursor selections + let comment_delimiter = + language.line_comment_prefix().filter(|_| is_cursor); + let comment_delimiter = if let Some(delimiter) = comment_delimiter { + buffer + .buffer_line_for_row(start_point.row) + .is_some_and(|(snapshot, range)| { + snapshot + .chars_for_range(range) + .skip_while(|c| c.is_whitespace()) + .take(delimiter.len()) + .eq(delimiter.chars()) + }) + .then(|| delimiter.clone()) + } else { + None + }; + (comment_delimiter, insert_extra_newline) + } else { + (None, false) + }; - enabled - && pair.newline - && buffer - .contains_str_at(end + trailing_whitespace_len, pair_end) - && buffer.contains_str_at( - (start - leading_whitespace_len) - .saturating_sub(pair_start.len()), - pair_start, - ) - }); - } - - let mut new_text = String::with_capacity(1 + indent.len as usize); - new_text.push('\n'); + let capacity_for_delimiter = comment_delimiter + .as_deref() + .map(str::len) + .unwrap_or_default(); + let mut new_text = + String::with_capacity(1 + capacity_for_delimiter + indent.len as usize); + new_text.push_str("\n"); new_text.extend(indent.chars()); + if let Some(delimiter) = &comment_delimiter { + new_text.push_str(&delimiter); + } if insert_extra_newline { new_text = new_text.repeat(2); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a63f3404d3..a94f01a386 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1719,6 +1719,33 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) { "}); } +#[gpui::test] +async fn test_newline_comments(cx: &mut gpui::TestAppContext) { + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4) + }); + + let language = Arc::new(Language::new( + LanguageConfig { + line_comment: Some("//".into()), + ..LanguageConfig::default() + }, + None, + )); + + let mut cx = EditorTestContext::new(cx).await; + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + cx.set_state(indoc! {" + // Fooˇ + "}); + + cx.update_editor(|e, cx| e.newline(&Newline, cx)); + cx.assert_editor_state(indoc! {" + // Foo + //ˇ + "}); +} + #[gpui::test] fn test_insert_with_old_selections(cx: &mut TestAppContext) { init_test(cx, |_| {}); From 8882b22c9c039bc647c60fe14268e2512f9b3b76 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 7 Jun 2023 11:57:41 -0400 Subject: [PATCH 125/131] v0.91.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 807ed777b2..9ac041b555 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8782,7 +8782,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.90.0" +version = "0.91.0" dependencies = [ "activity_indicator", "ai", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 54919445cb..ee29d976b3 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.90.0" +version = "0.91.0" publish = false [lib] From f56d642b88a550d5576ea6231b05eb0ad7c7e069 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 09:20:25 -0700 Subject: [PATCH 126/131] Disable scheduled randomized test workflow for now --- .github/workflows/randomized_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/randomized_tests.yml b/.github/workflows/randomized_tests.yml index d963ac46e0..aaef0b536d 100644 --- a/.github/workflows/randomized_tests.yml +++ b/.github/workflows/randomized_tests.yml @@ -6,8 +6,8 @@ on: push: branches: - randomized-tests-runner - schedule: - - cron: '0 * * * *' + # schedule: + # - cron: '0 * * * *' env: CARGO_TERM_COLOR: always From 6067575e389dce5870978f376f17a7f40299ef0c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 09:29:58 -0700 Subject: [PATCH 127/131] collab 0.13.0 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ac041b555..c07dcca8e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,7 +1239,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.12.5" +version = "0.13.0" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 07d2cdc60a..870f109a0a 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.12.5" +version = "0.13.0" publish = false [[bin]] From 6269cec4f18b3ffff4bcfbb601565a4d7ae13534 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 7 Jun 2023 12:40:49 -0400 Subject: [PATCH 128/131] Minor updates --- styles/src/buildLicenses.ts | 2 +- styles/src/buildThemes.ts | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 9827583aeb..2e1325044d 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -21,7 +21,7 @@ function parseAcceptedToml(file: string): string[] { function checkLicenses(themes: ThemeConfig[]) { for (const theme of themes) { if (!theme.licenseFile) { - throw Error(`Theme ${theme.name} should have a LICENSE files`) + throw Error(`Theme ${theme.name} should have a LICENSE file`) } } } diff --git a/styles/src/buildThemes.ts b/styles/src/buildThemes.ts index 7173f1cb0a..bba300989f 100644 --- a/styles/src/buildThemes.ts +++ b/styles/src/buildThemes.ts @@ -9,13 +9,6 @@ import { themes } from "./themes" const assetsDirectory = `${__dirname}/../../assets` const tempDirectory = fs.mkdtempSync(path.join(tmpdir(), "build-themes")) -function getColorSchemes() { - const colorSchemes: ColorScheme[] = themes.map((theme) => - createColorScheme(theme) - ) - return colorSchemes -} - // Clear existing themes function clearThemes(themeDirectory: string) { if (!fs.existsSync(themeDirectory)) { @@ -42,5 +35,7 @@ function writeThemes(colorSchemes: ColorScheme[], outputDirectory: string) { } } +const colorSchemes: ColorScheme[] = themes.map((theme) => createColorScheme(theme)) + // Write new themes to theme directory -writeThemes(getColorSchemes(), `${assetsDirectory}/themes`) +writeThemes(colorSchemes, `${assetsDirectory}/themes`) From 72372ddf0ed1fa18f57fcbec794acdb5270d8623 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 09:19:31 -0700 Subject: [PATCH 129/131] Highlight hover code blocks as LSP's own language by default The elixir LSP does not include a language name on its elixir code blocks. --- crates/editor/src/hover_popover.rs | 38 ++++++++++---------- crates/project/src/lsp_command.rs | 56 ++++++++++++++++++------------ crates/project/src/project.rs | 1 + 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 4c339e52df..7a203d54a9 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -221,6 +221,7 @@ fn show_hover( project: project.clone(), symbol_range: range, blocks: hover_result.contents, + language: hover_result.language, rendered_content: None, }) }); @@ -253,6 +254,7 @@ fn render_blocks( theme_id: usize, blocks: &[HoverBlock], language_registry: &Arc, + language: Option<&Arc>, style: &EditorStyle, ) -> RenderedInfo { let mut text = String::new(); @@ -351,11 +353,13 @@ fn render_blocks( } Tag::CodeBlock(kind) => { new_paragraph(&mut text, &mut list_stack); - if let CodeBlockKind::Fenced(language) = kind { - current_language = language_registry + current_language = if let CodeBlockKind::Fenced(language) = kind { + language_registry .language_for_name(language.as_ref()) .now_or_never() - .and_then(Result::ok); + .and_then(Result::ok) + } else { + language.cloned() } } Tag::Emphasis => italic_depth += 1, @@ -414,10 +418,6 @@ fn render_blocks( } } - if !text.is_empty() && !text.ends_with('\n') { - text.push('\n'); - } - RenderedInfo { theme_id, text, @@ -524,6 +524,7 @@ pub struct InfoPopover { pub project: ModelHandle, pub symbol_range: Range, pub blocks: Vec, + language: Option>, rendered_content: Option, } @@ -559,6 +560,7 @@ impl InfoPopover { style.theme_id, &self.blocks, self.project.read(cx).languages(), + self.language.as_ref(), style, ) }); @@ -903,7 +905,7 @@ mod tests { text: "one **two** three".to_string(), kind: HoverBlockKind::Markdown, }], - expected_marked_text: "one «two» three\n".to_string(), + expected_marked_text: "one «two» three".to_string(), expected_styles: vec![HighlightStyle { weight: Some(Weight::BOLD), ..Default::default() @@ -915,7 +917,7 @@ mod tests { text: "one [two](the-url) three".to_string(), kind: HoverBlockKind::Markdown, }], - expected_marked_text: "one «two» three\n".to_string(), + expected_marked_text: "one «two» three".to_string(), expected_styles: vec![HighlightStyle { underline: Some(Underline { thickness: 1.0.into(), @@ -934,8 +936,7 @@ mod tests { - b * two - [c](the-url) - - d - " + - d" .unindent(), kind: HoverBlockKind::Markdown, }], @@ -946,8 +947,7 @@ mod tests { - b - two - «c» - - d - " + - d" .unindent(), expected_styles: vec![HighlightStyle { underline: Some(Underline { @@ -970,9 +970,8 @@ mod tests { nine * ten - * six - " - .unindent(), + * six" + .unindent(), kind: HoverBlockKind::Markdown, }], expected_marked_text: " @@ -982,9 +981,8 @@ mod tests { nine - ten - - six - " - .unindent(), + - six" + .unindent(), expected_styles: vec![HighlightStyle { underline: Some(Underline { thickness: 1.0.into(), @@ -1001,7 +999,7 @@ mod tests { expected_styles, } in &rows[0..] { - let rendered = render_blocks(0, &blocks, &Default::default(), &style); + let rendered = render_blocks(0, &blocks, &Default::default(), None, &style); let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false); let expected_highlights = ranges diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 94a43d79ec..2d4bed760d 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1111,14 +1111,18 @@ impl LspCommand for GetHover { cx: AsyncAppContext, ) -> Result { Ok(message.and_then(|hover| { - let range = hover.range.map(|range| { - cx.read(|cx| { - let buffer = buffer.read(cx); - let token_start = - buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left); - let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left); - buffer.anchor_after(token_start)..buffer.anchor_before(token_end) - }) + let (language, range) = cx.read(|cx| { + let buffer = buffer.read(cx); + ( + buffer.language().cloned(), + hover.range.map(|range| { + let token_start = + buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left); + let token_end = + buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left); + buffer.anchor_after(token_start)..buffer.anchor_before(token_end) + }), + ) }); fn hover_blocks_from_marked_string( @@ -1163,7 +1167,11 @@ impl LspCommand for GetHover { }], }); - Some(Hover { contents, range }) + Some(Hover { + contents, + range, + language, + }) })) } @@ -1247,16 +1255,9 @@ impl LspCommand for GetHover { self, message: proto::GetHoverResponse, _: ModelHandle, - _: ModelHandle, - _: AsyncAppContext, + buffer: ModelHandle, + cx: AsyncAppContext, ) -> Result { - let range = if let (Some(start), Some(end)) = (message.start, message.end) { - language::proto::deserialize_anchor(start) - .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end)) - } else { - None - }; - let contents: Vec<_> = message .contents .into_iter() @@ -1271,12 +1272,23 @@ impl LspCommand for GetHover { }, }) .collect(); + if contents.is_empty() { + return Ok(None); + } - Ok(if contents.is_empty() { - None + let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned()); + let range = if let (Some(start), Some(end)) = (message.start, message.end) { + language::proto::deserialize_anchor(start) + .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end)) } else { - Some(Hover { contents, range }) - }) + None + }; + + Ok(Some(Hover { + contents, + range, + language, + })) } fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 39d8ea8512..2b2271bf1e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -359,6 +359,7 @@ pub enum HoverBlockKind { pub struct Hover { pub contents: Vec, pub range: Option>, + pub language: Option>, } #[derive(Default)] From be8d268eb9e6816e83fbc8843a88210c01324deb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 10:50:03 -0700 Subject: [PATCH 130/131] Bump tree-sitter-yaml submodule to avoid broken child submodule --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c07dcca8e1..15283c03da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7556,7 +7556,7 @@ dependencies = [ [[package]] name = "tree-sitter-yaml" version = "0.0.1" -source = "git+https://github.com/zed-industries/tree-sitter-yaml?rev=9050a4a4a847ed29e25485b1292a36eab8ae3492#9050a4a4a847ed29e25485b1292a36eab8ae3492" +source = "git+https://github.com/zed-industries/tree-sitter-yaml?rev=5694b7f290cd9ef998829a0a6d8391a666370886#5694b7f290cd9ef998829a0a6d8391a666370886" dependencies = [ "cc", "tree-sitter", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index ee29d976b3..81f89c0430 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -119,7 +119,7 @@ tree-sitter-ruby = "0.20.0" tree-sitter-html = "0.19.0" tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"} tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a"} -tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "9050a4a4a847ed29e25485b1292a36eab8ae3492"} +tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "5694b7f290cd9ef998829a0a6d8391a666370886"} tree-sitter-lua = "0.0.14" url = "2.2" urlencoding = "2.1.2" From 11ab1a8cc6ee16444178538526ec1e9ab6555389 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 10:54:27 -0700 Subject: [PATCH 131/131] collab 0.13.1 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15283c03da..c6d0987103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,7 +1239,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.13.0" +version = "0.13.1" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 870f109a0a..7058d04178 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.13.0" +version = "0.13.1" publish = false [[bin]]