From bca900a5d36e76c72866525ca2ee44318b028e7c Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 5 Jan 2024 00:40:08 -0500 Subject: [PATCH 01/54] Add Headline component --- crates/ui/src/components.rs | 2 + crates/ui/src/components/typography.rs | 71 ++++++++++++++++++++++++++ crates/ui/src/prelude.rs | 1 + 3 files changed, 74 insertions(+) create mode 100644 crates/ui/src/components/typography.rs diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index 0848ac74dfd594ab2dbf2a170d927a70b3ae386d..4049815f1b5abdfaf68144456c38f7c6f72b51c7 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -16,6 +16,7 @@ mod stack; mod tab; mod tab_bar; mod tooltip; +mod typography; #[cfg(feature = "stories")] mod stories; @@ -38,6 +39,7 @@ pub use stack::*; pub use tab::*; pub use tab_bar::*; pub use tooltip::*; +pub use typography::*; #[cfg(feature = "stories")] pub use stories::*; diff --git a/crates/ui/src/components/typography.rs b/crates/ui/src/components/typography.rs new file mode 100644 index 0000000000000000000000000000000000000000..c613559faf61ed352cb533767fa310c2b94041d8 --- /dev/null +++ b/crates/ui/src/components/typography.rs @@ -0,0 +1,71 @@ +use gpui::{ + div, rems, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, WindowContext, +}; +use settings::Settings; +use theme::{ActiveTheme, ThemeSettings}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] +pub enum HeadlineSize { + XSmall, + Small, + #[default] + Medium, + Large, + XLarge, +} + +impl HeadlineSize { + pub fn size(self) -> Rems { + match self { + // Based on the Major Second scale + Self::XSmall => rems(0.88), + Self::Small => rems(1.0), + Self::Medium => rems(1.125), + Self::Large => rems(1.27), + Self::XLarge => rems(1.43), + } + } + + pub fn line_height(self) -> Rems { + match self { + Self::XSmall => rems(1.6), + Self::Small => rems(1.6), + Self::Medium => rems(1.6), + Self::Large => rems(1.6), + Self::XLarge => rems(1.6), + } + } +} + +#[derive(IntoElement)] +pub struct Headline { + size: HeadlineSize, + text: SharedString, +} + +impl RenderOnce for Headline { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone(); + + div() + .font(ui_font) + .line_height(self.size.line_height()) + .text_size(self.size.size()) + .text_color(cx.theme().colors().text) + .child(self.text) + } +} + +impl Headline { + pub fn new(text: impl Into) -> Self { + Self { + size: HeadlineSize::default(), + text: text.into(), + } + } + + pub fn size(mut self, size: HeadlineSize) -> Self { + self.size = size; + self + } +} diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs index dbf3c79b7173c1c6d018216cb4e11aab444d2e54..9432e622a66138a7e3bb125b7597761b75fc1b88 100644 --- a/crates/ui/src/prelude.rs +++ b/crates/ui/src/prelude.rs @@ -14,6 +14,7 @@ pub use crate::visible_on_hover::*; pub use crate::{h_stack, v_stack}; pub use crate::{Button, ButtonSize, ButtonStyle, IconButton}; pub use crate::{ButtonCommon, Color, StyledExt}; +pub use crate::{Headline, HeadlineSize}; pub use crate::{Icon, IconElement, IconPosition, IconSize}; pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle}; pub use theme::ActiveTheme; From f7a036e95213cc4cf95bcfb4ce169ce95f9bacbd Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 5 Jan 2024 00:51:03 -0500 Subject: [PATCH 02/54] WIP - Start on Copilot Modal --- crates/copilot/src/sign_in.rs | 45 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index ba5dbe0e315828ac1761cf1d76ac37bb8211ea4c..4c736e930e0cf5f0a325a267af68295b37e68079 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -1,11 +1,10 @@ use crate::{request::PromptUserDeviceFlow, Copilot, Status}; use gpui::{ - div, size, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement, + div, size, svg, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement, IntoElement, ParentElement, Point, Render, Styled, ViewContext, VisualContext, WindowBounds, WindowHandle, WindowKind, WindowOptions, }; -use theme::ActiveTheme; -use ui::{prelude::*, Button, Icon, IconElement, Label}; +use ui::{prelude::*, Button, Icon, Label}; const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; @@ -59,7 +58,7 @@ fn create_copilot_auth_window( cx: &mut AppContext, status: &Status, ) -> WindowHandle { - let window_size = size(GlobalPixels::from(280.), GlobalPixels::from(280.)); + let window_size = size(GlobalPixels::from(400.), GlobalPixels::from(480.)); let window_options = WindowOptions { bounds: WindowBounds::Fixed(Bounds::new(Point::default(), window_size)), titlebar: None, @@ -129,8 +128,8 @@ impl CopilotCodeVerification { }; v_stack() .flex_1() + .gap_2() .items_center() - .justify_between() .w_full() .child(Label::new( "Enable Copilot by connecting your existing license", @@ -141,13 +140,16 @@ impl CopilotCodeVerification { .size(ui::LabelSize::Small), ) .child( - Button::new("connect-button", connect_button_label).on_click({ - let verification_uri = data.verification_uri.clone(); - cx.listener(move |this, _, cx| { - cx.open_url(&verification_uri); - this.connect_clicked = true; + Button::new("connect-button", connect_button_label) + .on_click({ + let verification_uri = data.verification_uri.clone(); + cx.listener(move |this, _, cx| { + cx.open_url(&verification_uri); + this.connect_clicked = true; + }) }) - }), + .full_width() + .style(ButtonStyle::Filled), ) } fn render_enabled_modal() -> impl Element { @@ -196,16 +198,23 @@ impl Render for CopilotCodeVerification { } _ => div().into_any_element(), }; - div() + + v_stack() .id("copilot code verification") - .flex() - .flex_col() + .elevation_3(cx) .size_full() .items_center() - .p_10() - .bg(cx.theme().colors().element_background) - .child(ui::Label::new("Connect Copilot to Zed")) - .child(IconElement::new(Icon::ZedXCopilot)) + .p_4() + .gap_4() + .child(Headline::new("Connect Copilot to Zed").size(HeadlineSize::Large)) + .child( + svg() + .w_32() + .h_16() + .flex_none() + .path(Icon::ZedXCopilot.path()) + .text_color(cx.theme().colors().icon), + ) .child(prompt) } } From 3e8e1c64045181d958fa4f32f3d89ecffa8dc0a4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:58:45 +0100 Subject: [PATCH 03/54] Move UI for copilot sign in to copilot_button --- Cargo.lock | 2 +- crates/copilot/Cargo.toml | 1 - crates/copilot/src/copilot.rs | 3 --- crates/copilot_button/Cargo.toml | 1 + crates/copilot_button/src/copilot_button.rs | 6 ++++++ crates/{copilot => copilot_button}/src/sign_in.rs | 3 ++- crates/zed/src/main.rs | 1 + 7 files changed, 11 insertions(+), 6 deletions(-) rename crates/{copilot => copilot_button}/src/sign_in.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 0316c343cb4ea5922897d9fc8ab417793e770ade..e0bfcc79b63f38e03a6984d21d7e8fdba85cb329 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1684,7 +1684,6 @@ dependencies = [ "settings", "smol", "theme", - "ui", "util", ] @@ -1702,6 +1701,7 @@ dependencies = [ "settings", "smol", "theme", + "ui", "util", "workspace", "zed_actions", diff --git a/crates/copilot/Cargo.toml b/crates/copilot/Cargo.toml index 588c747696c3ed58696bd12fbe97f3cbf3f9ef8a..fefd49090fd2020f4dc6da8aa1c2a1c5d119ea96 100644 --- a/crates/copilot/Cargo.toml +++ b/crates/copilot/Cargo.toml @@ -28,7 +28,6 @@ theme = { path = "../theme" } lsp = { path = "../lsp" } node_runtime = { path = "../node_runtime"} util = { path = "../util" } -ui = { path = "../ui" } async-compression.workspace = true async-tar = "0.4.2" anyhow.workspace = true diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 658eb3451f473d4a9af26053dae20f21479c339e..89d1086c8e4b897c3527964289ff11810078a57c 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -1,6 +1,4 @@ pub mod request; -mod sign_in; - use anyhow::{anyhow, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; @@ -98,7 +96,6 @@ pub fn init( }) .detach(); - sign_in::init(cx); cx.on_action(|_: &SignIn, cx| { if let Some(copilot) = Copilot::global(cx) { copilot diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_button/Cargo.toml index 63788f9d28a5097bab1f02ab340c253b704cc599..e166e760c13dc8faddfd45f9712ebfb84c826393 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_button/Cargo.toml @@ -17,6 +17,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } settings = { path = "../settings" } theme = { path = "../theme" } +ui = { path = "../ui" } util = { path = "../util" } workspace = {path = "../workspace" } anyhow.workspace = true diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 60b25fee12ab8c32ca6894fb2363f271f27246a4..3b561da2f142a771157f4ed3c141c41768428cb4 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -1,3 +1,5 @@ +mod sign_in; + use anyhow::Result; use copilot::{Copilot, SignOut, Status}; use editor::{scroll::autoscroll::Autoscroll, Editor}; @@ -25,6 +27,10 @@ const COPILOT_SETTINGS_URL: &str = "https://github.com/settings/copilot"; const COPILOT_STARTING_TOAST_ID: usize = 1337; const COPILOT_ERROR_TOAST_ID: usize = 1338; +pub fn init(cx: &mut AppContext) { + sign_in::init(cx); +} + pub struct CopilotButton { editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot_button/src/sign_in.rs similarity index 98% rename from crates/copilot/src/sign_in.rs rename to crates/copilot_button/src/sign_in.rs index 4c736e930e0cf5f0a325a267af68295b37e68079..a632ce08d050dbc1f10d9a163da23afa0fedc9ff 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot_button/src/sign_in.rs @@ -1,4 +1,4 @@ -use crate::{request::PromptUserDeviceFlow, Copilot, Status}; +use copilot::{request::PromptUserDeviceFlow, Copilot, Status}; use gpui::{ div, size, svg, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement, IntoElement, ParentElement, Point, Render, Styled, ViewContext, VisualContext, WindowBounds, @@ -80,6 +80,7 @@ pub struct CopilotCodeVerification { connect_clicked: bool, } +//impl ModalView for CopilotCodeVerification {} impl CopilotCodeVerification { pub fn new(status: Status) -> Self { Self { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e0da81edc4ae17702b55a306bae3ec8b9d7a2bfd..e41baee4aea80c39bf5153f15f6fb1f80db4bb53 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -158,6 +158,7 @@ fn main() { node_runtime.clone(), cx, ); + copilot_button::init(cx); assistant::init(cx); cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) From 0ce94fc791af44e0bce3f2202904f2f6585e6b7f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:27:52 +0100 Subject: [PATCH 04/54] Convert copilot popup to modal --- crates/copilot_button/src/copilot_button.rs | 16 +-- crates/copilot_button/src/sign_in.rs | 109 ++++++-------------- crates/zed/src/main.rs | 1 - 3 files changed, 39 insertions(+), 87 deletions(-) diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 3b561da2f142a771157f4ed3c141c41768428cb4..6bf1d6cfcc9a1a7b614857e04952e6e301a71745 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -13,6 +13,7 @@ use language::{ File, Language, }; use settings::{update_settings_file, Settings, SettingsStore}; +use sign_in::CopilotCodeVerification; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; use workspace::{ @@ -27,10 +28,6 @@ const COPILOT_SETTINGS_URL: &str = "https://github.com/settings/copilot"; const COPILOT_STARTING_TOAST_ID: usize = 1337; const COPILOT_ERROR_TOAST_ID: usize = 1338; -pub fn init(cx: &mut AppContext) { - sign_in::init(cx); -} - pub struct CopilotButton { editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, @@ -337,7 +334,9 @@ fn initiate_sign_in(cx: &mut WindowContext) { return; }; let status = copilot.read(cx).status(); - + let Some(workspace) = cx.window_handle().downcast::() else { + return; + }; match status { Status::Starting { task } => { let Some(workspace) = cx.window_handle().downcast::() else { @@ -376,9 +375,10 @@ fn initiate_sign_in(cx: &mut WindowContext) { .detach(); } _ => { - copilot - .update(cx, |copilot, cx| copilot.sign_in(cx)) - .detach_and_log_err(cx); + copilot.update(cx, |this, cx| this.sign_in(cx)).detach(); + workspace.update(cx, |this, cx| { + this.toggle_modal(cx, |cx| CopilotCodeVerification::new(&copilot, cx)); + }); } } } diff --git a/crates/copilot_button/src/sign_in.rs b/crates/copilot_button/src/sign_in.rs index a632ce08d050dbc1f10d9a163da23afa0fedc9ff..15e68f961e9c1f099cd456956b8d3c7552d3d7d4 100644 --- a/crates/copilot_button/src/sign_in.rs +++ b/crates/copilot_button/src/sign_in.rs @@ -1,91 +1,49 @@ use copilot::{request::PromptUserDeviceFlow, Copilot, Status}; use gpui::{ - div, size, svg, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement, - IntoElement, ParentElement, Point, Render, Styled, ViewContext, VisualContext, WindowBounds, + div, size, svg, AppContext, Bounds, ClipboardItem, DismissEvent, Element, EventEmitter, + FocusHandle, FocusableView, GlobalPixels, InteractiveElement, IntoElement, Model, + ParentElement, Point, Render, Styled, Subscription, ViewContext, VisualContext, WindowBounds, WindowHandle, WindowKind, WindowOptions, }; use ui::{prelude::*, Button, Icon, Label}; +use workspace::ModalView; const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; -pub fn init(cx: &mut AppContext) { - if let Some(copilot) = Copilot::global(cx) { - let mut verification_window: Option> = None; - cx.observe(&copilot, move |copilot, cx| { - let status = copilot.read(cx).status(); - - match &status { - crate::Status::SigningIn { prompt } => { - if let Some(window) = verification_window.as_mut() { - let updated = window - .update(cx, |verification, cx| { - verification.set_status(status.clone(), cx); - cx.activate_window(); - }) - .is_ok(); - if !updated { - verification_window = Some(create_copilot_auth_window(cx, &status)); - } - } else if let Some(_prompt) = prompt { - verification_window = Some(create_copilot_auth_window(cx, &status)); - } - } - Status::Authorized | Status::Unauthorized => { - if let Some(window) = verification_window.as_ref() { - window - .update(cx, |verification, cx| { - verification.set_status(status, cx); - cx.activate(true); - cx.activate_window(); - }) - .ok(); - } - } - _ => { - if let Some(code_verification) = verification_window.take() { - code_verification - .update(cx, |_, cx| cx.remove_window()) - .ok(); - } - } - } - }) - .detach(); - } -} - -fn create_copilot_auth_window( - cx: &mut AppContext, - status: &Status, -) -> WindowHandle { - let window_size = size(GlobalPixels::from(400.), GlobalPixels::from(480.)); - let window_options = WindowOptions { - bounds: WindowBounds::Fixed(Bounds::new(Point::default(), window_size)), - titlebar: None, - center: true, - focus: true, - show: true, - kind: WindowKind::PopUp, - is_movable: true, - display_id: None, - }; - let window = cx.open_window(window_options, |cx| { - cx.new_view(|_| CopilotCodeVerification::new(status.clone())) - }); - window -} +pub fn init(cx: &mut AppContext) {} pub struct CopilotCodeVerification { status: Status, connect_clicked: bool, + focus_handle: FocusHandle, + _subscription: Subscription, +} + +impl FocusableView for CopilotCodeVerification { + fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + self.focus_handle.clone() + } } -//impl ModalView for CopilotCodeVerification {} +impl EventEmitter for CopilotCodeVerification {} +impl ModalView for CopilotCodeVerification {} + impl CopilotCodeVerification { - pub fn new(status: Status) -> Self { + pub(crate) fn new(copilot: &Model, cx: &mut ViewContext) -> Self { + let status = copilot.read(cx).status(); Self { status, connect_clicked: false, + focus_handle: cx.focus_handle(), + _subscription: cx.observe(copilot, |this, copilot, cx| { + let status = copilot.read(cx).status(); + match status { + Status::Authorized | Status::Unauthorized | Status::SigningIn { .. } => { + this.set_status(status, cx) + } + _ => cx.emit(DismissEvent), + } + }), } } @@ -159,10 +117,7 @@ impl CopilotCodeVerification { .child(Label::new( "You can update your settings or sign out from the Copilot menu in the status bar.", )) - .child( - Button::new("copilot-enabled-done-button", "Done") - .on_click(|_, cx| cx.remove_window()), - ) + .child(Button::new("copilot-enabled-done-button", "Done").on_click(|_, cx| {})) } fn render_unauthorized_modal() -> impl Element { @@ -175,10 +130,8 @@ impl CopilotCodeVerification { .color(Color::Warning), ) .child( - Button::new("copilot-subscribe-button", "Subscibe on Github").on_click(|_, cx| { - cx.remove_window(); - cx.open_url(COPILOT_SIGN_UP_URL) - }), + Button::new("copilot-subscribe-button", "Subscibe on Github") + .on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)), ) } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e41baee4aea80c39bf5153f15f6fb1f80db4bb53..e0da81edc4ae17702b55a306bae3ec8b9d7a2bfd 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -158,7 +158,6 @@ fn main() { node_runtime.clone(), cx, ); - copilot_button::init(cx); assistant::init(cx); cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) From 0670a6f838e0d869b8607e5f3dd605373cdd68ff Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:32:47 +0100 Subject: [PATCH 05/54] Fix up warnings, bind 'Done' button to DismissEvent --- crates/copilot_button/src/copilot_button.rs | 8 +++++--- crates/copilot_button/src/sign_in.rs | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 6bf1d6cfcc9a1a7b614857e04952e6e301a71745..2a1bde785e0f69c2ddef8041ff0c2177d528ebed 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -376,9 +376,11 @@ fn initiate_sign_in(cx: &mut WindowContext) { } _ => { copilot.update(cx, |this, cx| this.sign_in(cx)).detach(); - workspace.update(cx, |this, cx| { - this.toggle_modal(cx, |cx| CopilotCodeVerification::new(&copilot, cx)); - }); + workspace + .update(cx, |this, cx| { + this.toggle_modal(cx, |cx| CopilotCodeVerification::new(&copilot, cx)); + }) + .ok(); } } } diff --git a/crates/copilot_button/src/sign_in.rs b/crates/copilot_button/src/sign_in.rs index 15e68f961e9c1f099cd456956b8d3c7552d3d7d4..d3e52c3e19db9c491fcf6091a4db67d6753d270b 100644 --- a/crates/copilot_button/src/sign_in.rs +++ b/crates/copilot_button/src/sign_in.rs @@ -1,17 +1,14 @@ use copilot::{request::PromptUserDeviceFlow, Copilot, Status}; use gpui::{ - div, size, svg, AppContext, Bounds, ClipboardItem, DismissEvent, Element, EventEmitter, - FocusHandle, FocusableView, GlobalPixels, InteractiveElement, IntoElement, Model, - ParentElement, Point, Render, Styled, Subscription, ViewContext, VisualContext, WindowBounds, - WindowHandle, WindowKind, WindowOptions, + div, svg, AppContext, ClipboardItem, DismissEvent, Element, EventEmitter, FocusHandle, + FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, + Subscription, ViewContext, }; use ui::{prelude::*, Button, Icon, Label}; use workspace::ModalView; const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; -pub fn init(cx: &mut AppContext) {} - pub struct CopilotCodeVerification { status: Status, connect_clicked: bool, @@ -20,7 +17,7 @@ pub struct CopilotCodeVerification { } impl FocusableView for CopilotCodeVerification { - fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { self.focus_handle.clone() } } @@ -111,13 +108,16 @@ impl CopilotCodeVerification { .style(ButtonStyle::Filled), ) } - fn render_enabled_modal() -> impl Element { + fn render_enabled_modal(cx: &mut ViewContext) -> impl Element { v_stack() .child(Label::new("Copilot Enabled!")) .child(Label::new( "You can update your settings or sign out from the Copilot menu in the status bar.", )) - .child(Button::new("copilot-enabled-done-button", "Done").on_click(|_, cx| {})) + .child( + Button::new("copilot-enabled-done-button", "Done") + .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))), + ) } fn render_unauthorized_modal() -> impl Element { @@ -148,7 +148,7 @@ impl Render for CopilotCodeVerification { } Status::Authorized => { self.connect_clicked = false; - Self::render_enabled_modal().into_any_element() + Self::render_enabled_modal(cx).into_any_element() } _ => div().into_any_element(), }; From 0602953af457c24574d654d2137c56822bc7611b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:36:26 +0100 Subject: [PATCH 06/54] Rename copilot_button crate to copilot_ui --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- crates/{copilot_button => copilot_ui}/Cargo.toml | 4 ++-- crates/{copilot_button => copilot_ui}/src/copilot_button.rs | 4 +--- crates/copilot_ui/src/copilot_ui.rs | 5 +++++ crates/{copilot_button => copilot_ui}/src/sign_in.rs | 0 crates/zed/Cargo.toml | 2 +- crates/zed/src/zed.rs | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) rename crates/{copilot_button => copilot_ui}/Cargo.toml (91%) rename crates/{copilot_button => copilot_ui}/src/copilot_button.rs (99%) create mode 100644 crates/copilot_ui/src/copilot_ui.rs rename crates/{copilot_button => copilot_ui}/src/sign_in.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index e0bfcc79b63f38e03a6984d21d7e8fdba85cb329..ac167dcc7b1778cb955cbe4c8e8ba38dc51cca7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1688,7 +1688,7 @@ dependencies = [ ] [[package]] -name = "copilot_button" +name = "copilot_ui" version = "0.1.0" dependencies = [ "anyhow", @@ -9537,7 +9537,7 @@ dependencies = [ "collections", "command_palette", "copilot", - "copilot_button", + "copilot_ui", "ctor", "db", "diagnostics", diff --git a/Cargo.toml b/Cargo.toml index 9f2bc145901cdcfa4fc7c02649edcdbbbb73dc07..fa21cc5364b34c639e8faa419448943d4ba5cd3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ "crates/collections", "crates/command_palette", "crates/copilot", - "crates/copilot_button", + "crates/copilot_ui", "crates/db", "crates/refineable", "crates/refineable/derive_refineable", diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_ui/Cargo.toml similarity index 91% rename from crates/copilot_button/Cargo.toml rename to crates/copilot_ui/Cargo.toml index e166e760c13dc8faddfd45f9712ebfb84c826393..491f4f3cdec3d2ebd20fe1d6a2536471f862b90b 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_ui/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "copilot_button" +name = "copilot_ui" version = "0.1.0" edition = "2021" publish = false [lib] -path = "src/copilot_button.rs" +path = "src/copilot_ui.rs" doctest = false [dependencies] diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs similarity index 99% rename from crates/copilot_button/src/copilot_button.rs rename to crates/copilot_ui/src/copilot_button.rs index 2a1bde785e0f69c2ddef8041ff0c2177d528ebed..e55f45c29333edbecc22742fccf0d4cd10a4a0df 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -1,5 +1,4 @@ -mod sign_in; - +use crate::sign_in::CopilotCodeVerification; use anyhow::Result; use copilot::{Copilot, SignOut, Status}; use editor::{scroll::autoscroll::Autoscroll, Editor}; @@ -13,7 +12,6 @@ use language::{ File, Language, }; use settings::{update_settings_file, Settings, SettingsStore}; -use sign_in::CopilotCodeVerification; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; use workspace::{ diff --git a/crates/copilot_ui/src/copilot_ui.rs b/crates/copilot_ui/src/copilot_ui.rs new file mode 100644 index 0000000000000000000000000000000000000000..64dd068d5aff5f0910e0ed78ea2746f0f6540189 --- /dev/null +++ b/crates/copilot_ui/src/copilot_ui.rs @@ -0,0 +1,5 @@ +mod copilot_button; +mod sign_in; + +pub use copilot_button::*; +pub use sign_in::*; diff --git a/crates/copilot_button/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs similarity index 100% rename from crates/copilot_button/src/sign_in.rs rename to crates/copilot_ui/src/sign_in.rs diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 39ab5e285b77672e31d8b33555f83bd768e4be68..2d6ad999a55731fee0eb09a1f64e471a20edd7fd 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -30,7 +30,7 @@ command_palette = { path = "../command_palette" } client = { path = "../client" } # clock = { path = "../clock" } copilot = { path = "../copilot" } -copilot_button = { path = "../copilot_button" } +copilot_ui = { path = "../copilot_ui" } diagnostics = { path = "../diagnostics" } db = { path = "../db" } editor = { path = "../editor" } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c7d30230ea035b29121ceb487e4ea3ce483d5d54..f0634081f3491439b8a662422acd9d641966eb4e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -123,7 +123,7 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { // workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx); let copilot = - cx.new_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx)); + cx.new_view(|cx| copilot_ui::CopilotButton::new(app_state.fs.clone(), cx)); let diagnostic_summary = cx.new_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx)); let activity_indicator = From 61ebb9fb378459807c1906643a77f494b628655c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:43:35 +0100 Subject: [PATCH 07/54] cargo fmt --- crates/zed/src/zed.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f0634081f3491439b8a662422acd9d641966eb4e..d5efa0f26380ef3d521f705d8acd437e4833426f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -122,8 +122,7 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { // cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx)); // workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx); - let copilot = - cx.new_view(|cx| copilot_ui::CopilotButton::new(app_state.fs.clone(), cx)); + let copilot = cx.new_view(|cx| copilot_ui::CopilotButton::new(app_state.fs.clone(), cx)); let diagnostic_summary = cx.new_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx)); let activity_indicator = From d1445431f2deb6fb7225c81d8966b72ba3729f64 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 5 Jan 2024 15:59:34 -0500 Subject: [PATCH 08/54] Use the already existing styles/typography for Headline --- crates/ui/src/components.rs | 2 - crates/ui/src/components/typography.rs | 71 ------------------------- crates/ui/src/styles/typography.rs | 72 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 74 deletions(-) delete mode 100644 crates/ui/src/components/typography.rs diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index 4049815f1b5abdfaf68144456c38f7c6f72b51c7..0848ac74dfd594ab2dbf2a170d927a70b3ae386d 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -16,7 +16,6 @@ mod stack; mod tab; mod tab_bar; mod tooltip; -mod typography; #[cfg(feature = "stories")] mod stories; @@ -39,7 +38,6 @@ pub use stack::*; pub use tab::*; pub use tab_bar::*; pub use tooltip::*; -pub use typography::*; #[cfg(feature = "stories")] pub use stories::*; diff --git a/crates/ui/src/components/typography.rs b/crates/ui/src/components/typography.rs deleted file mode 100644 index c613559faf61ed352cb533767fa310c2b94041d8..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/typography.rs +++ /dev/null @@ -1,71 +0,0 @@ -use gpui::{ - div, rems, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, WindowContext, -}; -use settings::Settings; -use theme::{ActiveTheme, ThemeSettings}; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] -pub enum HeadlineSize { - XSmall, - Small, - #[default] - Medium, - Large, - XLarge, -} - -impl HeadlineSize { - pub fn size(self) -> Rems { - match self { - // Based on the Major Second scale - Self::XSmall => rems(0.88), - Self::Small => rems(1.0), - Self::Medium => rems(1.125), - Self::Large => rems(1.27), - Self::XLarge => rems(1.43), - } - } - - pub fn line_height(self) -> Rems { - match self { - Self::XSmall => rems(1.6), - Self::Small => rems(1.6), - Self::Medium => rems(1.6), - Self::Large => rems(1.6), - Self::XLarge => rems(1.6), - } - } -} - -#[derive(IntoElement)] -pub struct Headline { - size: HeadlineSize, - text: SharedString, -} - -impl RenderOnce for Headline { - fn render(self, cx: &mut WindowContext) -> impl IntoElement { - let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone(); - - div() - .font(ui_font) - .line_height(self.size.line_height()) - .text_size(self.size.size()) - .text_color(cx.theme().colors().text) - .child(self.text) - } -} - -impl Headline { - pub fn new(text: impl Into) -> Self { - Self { - size: HeadlineSize::default(), - text: text.into(), - } - } - - pub fn size(mut self, size: HeadlineSize) -> Self { - self.size = size; - self - } -} diff --git a/crates/ui/src/styles/typography.rs b/crates/ui/src/styles/typography.rs index 4819791b02c988bfccc1c59484b373e5d8249bfe..39937ebff1701f2195c4112235c919368b97b364 100644 --- a/crates/ui/src/styles/typography.rs +++ b/crates/ui/src/styles/typography.rs @@ -1,4 +1,8 @@ -use gpui::{rems, Rems}; +use gpui::{ + div, rems, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, WindowContext, +}; +use settings::Settings; +use theme::{ActiveTheme, ThemeSettings}; #[derive(Debug, Default, Clone)] pub enum UiTextSize { @@ -33,3 +37,69 @@ impl UiTextSize { } } } + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] +pub enum HeadlineSize { + XSmall, + Small, + #[default] + Medium, + Large, + XLarge, +} + +impl HeadlineSize { + pub fn size(self) -> Rems { + match self { + // Based on the Major Second scale + Self::XSmall => rems(0.88), + Self::Small => rems(1.0), + Self::Medium => rems(1.125), + Self::Large => rems(1.27), + Self::XLarge => rems(1.43), + } + } + + pub fn line_height(self) -> Rems { + match self { + Self::XSmall => rems(1.6), + Self::Small => rems(1.6), + Self::Medium => rems(1.6), + Self::Large => rems(1.6), + Self::XLarge => rems(1.6), + } + } +} + +#[derive(IntoElement)] +pub struct Headline { + size: HeadlineSize, + text: SharedString, +} + +impl RenderOnce for Headline { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone(); + + div() + .font(ui_font) + .line_height(self.size.line_height()) + .text_size(self.size.size()) + .text_color(cx.theme().colors().text) + .child(self.text) + } +} + +impl Headline { + pub fn new(text: impl Into) -> Self { + Self { + size: HeadlineSize::default(), + text: text.into(), + } + } + + pub fn size(mut self, size: HeadlineSize) -> Self { + self.size = size; + self + } +} From 76b1a3ca0ef166d0fad52e0b2d55fab9b32066e3 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 5 Jan 2024 16:17:06 -0500 Subject: [PATCH 09/54] Refine copilot UI --- crates/copilot_ui/src/sign_in.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/copilot_ui/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs index d3e52c3e19db9c491fcf6091a4db67d6753d270b..029ad149b8d07df22ea7b4bcf97738e2b4631f84 100644 --- a/crates/copilot_ui/src/sign_in.rs +++ b/crates/copilot_ui/src/sign_in.rs @@ -86,10 +86,11 @@ impl CopilotCodeVerification { .flex_1() .gap_2() .items_center() - .w_full() - .child(Label::new( - "Enable Copilot by connecting your existing license", - )) + .child(Headline::new("Use Github Copilot in Zed.").size(HeadlineSize::Large)) + .child( + Label::new("Using Copilot requres an active subscription on Github.") + .color(Color::Muted), + ) .child(Self::render_device_code(data, cx)) .child( Label::new("Paste this code into GitHub after clicking the button below.") @@ -156,11 +157,10 @@ impl Render for CopilotCodeVerification { v_stack() .id("copilot code verification") .elevation_3(cx) - .size_full() + .w_96() .items_center() .p_4() - .gap_4() - .child(Headline::new("Connect Copilot to Zed").size(HeadlineSize::Large)) + .gap_2() .child( svg() .w_32() From c40e45e4d74175d444ff18fe131c7ed359d6f107 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Mon, 8 Jan 2024 11:34:00 -0500 Subject: [PATCH 10/54] Use default instead of muted color --- crates/diagnostics/src/items.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index da1f77b9afb0f1c4308362f42f479dc30890e77b..a250713e65eb4e13c12a8981b731d6b9233b427a 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -25,11 +25,7 @@ impl Render for DiagnosticIndicator { let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) { (0, 0) => h_stack().map(|this| { if !self.in_progress_checks.is_empty() { - this.child( - IconElement::new(Icon::ArrowCircle) - .size(IconSize::Small) - .color(Color::Muted), - ) + this.child(IconElement::new(Icon::ArrowCircle).size(IconSize::Small)) } else { this.child( IconElement::new(Icon::Check) @@ -74,7 +70,6 @@ impl Render for DiagnosticIndicator { Some( Label::new("Checking…") .size(LabelSize::Small) - .color(Color::Muted) .into_any_element(), ) } else if let Some(diagnostic) = &self.current_diagnostic { From 1ede003de20822d7a1419e125db8e7bc974e8621 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Mon, 8 Jan 2024 11:55:51 -0500 Subject: [PATCH 11/54] Always show checking with icon if checks are still running --- assets/icons/arrow_circle.svg | 7 ++++++- crates/diagnostics/src/items.rs | 24 +++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/assets/icons/arrow_circle.svg b/assets/icons/arrow_circle.svg index 750e349e2b8c73ef0c78b9974ea100f70ae37abe..90e352bdea7a208356139bed8af5bb3c1301b5ce 100644 --- a/assets/icons/arrow_circle.svg +++ b/assets/icons/arrow_circle.svg @@ -1 +1,6 @@ - + + + + + + diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index a250713e65eb4e13c12a8981b731d6b9233b427a..0c2d673d8e68b5bd43681788bde078442ed43d9d 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -24,15 +24,11 @@ impl Render for DiagnosticIndicator { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) { (0, 0) => h_stack().map(|this| { - if !self.in_progress_checks.is_empty() { - this.child(IconElement::new(Icon::ArrowCircle).size(IconSize::Small)) - } else { - this.child( - IconElement::new(Icon::Check) - .size(IconSize::Small) - .color(Color::Default), - ) - } + this.child( + IconElement::new(Icon::Check) + .size(IconSize::Small) + .color(Color::Default), + ) }), (0, warning_count) => h_stack() .gap_1() @@ -68,8 +64,14 @@ impl Render for DiagnosticIndicator { let status = if !self.in_progress_checks.is_empty() { Some( - Label::new("Checking…") - .size(LabelSize::Small) + h_stack() + .gap_2() + .child(IconElement::new(Icon::ArrowCircle).size(IconSize::Small)) + .child( + Label::new("Checking…") + .size(LabelSize::Small) + .into_any_element(), + ) .into_any_element(), ) } else if let Some(diagnostic) = &self.current_diagnostic { From 04f01ab40821da8bdb1642aa0fc7c44ec3600cdd Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 12:01:13 -0500 Subject: [PATCH 12/54] Overdraw the tree branch to avoid gaps --- crates/collab_ui/src/collab_panel.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index ee43b32f106e8228806a1ca6108b05b00038ca22..ff87bb8b66ca4b04516026c496a726765c9b8d80 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -896,7 +896,7 @@ impl CollabPanel { .start_slot( h_stack() .gap_1() - .child(render_tree_branch(is_last, cx)) + .child(render_tree_branch(is_last, false, cx)) .child(IconButton::new(0, Icon::Folder)), ) .child(Label::new(project_name.clone())) @@ -917,7 +917,7 @@ impl CollabPanel { .start_slot( h_stack() .gap_1() - .child(render_tree_branch(is_last, cx)) + .child(render_tree_branch(is_last, false, cx)) .child(IconButton::new(0, Icon::Screen)), ) .child(Label::new("Screen")) @@ -958,7 +958,7 @@ impl CollabPanel { .start_slot( h_stack() .gap_1() - .child(render_tree_branch(false, cx)) + .child(render_tree_branch(false, true, cx)) .child(IconButton::new(0, Icon::File)), ) .child(div().h_7().w_full().child(Label::new("notes"))) @@ -979,7 +979,7 @@ impl CollabPanel { .start_slot( h_stack() .gap_1() - .child(render_tree_branch(false, cx)) + .child(render_tree_branch(false, false, cx)) .child(IconButton::new(0, Icon::MessageBubbles)), ) .child(Label::new("chat")) @@ -1007,7 +1007,7 @@ impl CollabPanel { .start_slot( h_stack() .gap_1() - .child(render_tree_branch(!has_visible_participants, cx)) + .child(render_tree_branch(!has_visible_participants, false, cx)) .child(""), ) .child(Label::new(if count == 1 { @@ -2404,7 +2404,7 @@ impl CollabPanel { } } -fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement { +fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) -> impl IntoElement { let rem_size = cx.rem_size(); let line_height = cx.text_style().line_height_in_pixels(rem_size); let width = rem_size * 1.5; @@ -2422,7 +2422,11 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement point(start_x, top), point( start_x + thickness, - if is_last { start_y } else { bounds.bottom() }, + if is_last { + start_y + } else { + bounds.bottom() + if overdraw { px(1.) } else { px(0.) } + }, ), ), color, From d3c9626169f222cf7e2ea650c92c955a48ef473f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:29:14 +0100 Subject: [PATCH 13/54] Comment out test_open_paths_action pending investigation (#3939) Commenting this one out temporarily to not break CI for folks while I look into it. Release Notes: - N/A --- crates/zed/src/zed.rs | 208 +++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 702c815d34600b8502e55f83f60b17c572dc869e..61b8d6eaf84332474ff992e5a72a85cbde5b4776 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -791,110 +791,110 @@ mod tests { WorkspaceHandle, }; - #[gpui::test] - async fn test_open_paths_action(cx: &mut TestAppContext) { - let app_state = init_test(cx); - app_state - .fs - .as_fake() - .insert_tree( - "/root", - json!({ - "a": { - "aa": null, - "ab": null, - }, - "b": { - "ba": null, - "bb": null, - }, - "c": { - "ca": null, - "cb": null, - }, - "d": { - "da": null, - "db": null, - }, - }), - ) - .await; - - cx.update(|cx| { - open_paths( - &[PathBuf::from("/root/a"), PathBuf::from("/root/b")], - &app_state, - None, - cx, - ) - }) - .await - .unwrap(); - assert_eq!(cx.read(|cx| cx.windows().len()), 1); - - cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) - .await - .unwrap(); - assert_eq!(cx.read(|cx| cx.windows().len()), 1); - let workspace_1 = cx - .read(|cx| cx.windows()[0].downcast::()) - .unwrap(); - workspace_1 - .update(cx, |workspace, cx| { - assert_eq!(workspace.worktrees(cx).count(), 2); - assert!(workspace.left_dock().read(cx).is_open()); - assert!(workspace - .active_pane() - .read(cx) - .focus_handle(cx) - .is_focused(cx)); - }) - .unwrap(); - - cx.update(|cx| { - open_paths( - &[PathBuf::from("/root/b"), PathBuf::from("/root/c")], - &app_state, - None, - cx, - ) - }) - .await - .unwrap(); - assert_eq!(cx.read(|cx| cx.windows().len()), 2); - - // Replace existing windows - let window = cx - .update(|cx| cx.windows()[0].downcast::()) - .unwrap(); - cx.update(|cx| { - open_paths( - &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], - &app_state, - Some(window), - cx, - ) - }) - .await - .unwrap(); - assert_eq!(cx.read(|cx| cx.windows().len()), 2); - let workspace_1 = cx - .update(|cx| cx.windows()[0].downcast::()) - .unwrap(); - workspace_1 - .update(cx, |workspace, cx| { - assert_eq!( - workspace - .worktrees(cx) - .map(|w| w.read(cx).abs_path()) - .collect::>(), - &[Path::new("/root/c").into(), Path::new("/root/d").into()] - ); - assert!(workspace.left_dock().read(cx).is_open()); - assert!(workspace.active_pane().focus_handle(cx).is_focused(cx)); - }) - .unwrap(); - } + // #[gpui::test] + // async fn test_open_paths_action(cx: &mut TestAppContext) { + // let app_state = init_test(cx); + // app_state + // .fs + // .as_fake() + // .insert_tree( + // "/root", + // json!({ + // "a": { + // "aa": null, + // "ab": null, + // }, + // "b": { + // "ba": null, + // "bb": null, + // }, + // "c": { + // "ca": null, + // "cb": null, + // }, + // "d": { + // "da": null, + // "db": null, + // }, + // }), + // ) + // .await; + + // cx.update(|cx| { + // open_paths( + // &[PathBuf::from("/root/a"), PathBuf::from("/root/b")], + // &app_state, + // None, + // cx, + // ) + // }) + // .await + // .unwrap(); + // assert_eq!(cx.read(|cx| cx.windows().len()), 1); + + // cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) + // .await + // .unwrap(); + // assert_eq!(cx.read(|cx| cx.windows().len()), 1); + // let workspace_1 = cx + // .read(|cx| cx.windows()[0].downcast::()) + // .unwrap(); + // workspace_1 + // .update(cx, |workspace, cx| { + // assert_eq!(workspace.worktrees(cx).count(), 2); + // assert!(workspace.left_dock().read(cx).is_open()); + // assert!(workspace + // .active_pane() + // .read(cx) + // .focus_handle(cx) + // .is_focused(cx)); + // }) + // .unwrap(); + + // cx.update(|cx| { + // open_paths( + // &[PathBuf::from("/root/b"), PathBuf::from("/root/c")], + // &app_state, + // None, + // cx, + // ) + // }) + // .await + // .unwrap(); + // assert_eq!(cx.read(|cx| cx.windows().len()), 2); + + // // Replace existing windows + // let window = cx + // .update(|cx| cx.windows()[0].downcast::()) + // .unwrap(); + // cx.update(|cx| { + // open_paths( + // &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], + // &app_state, + // Some(window), + // cx, + // ) + // }) + // .await + // .unwrap(); + // assert_eq!(cx.read(|cx| cx.windows().len()), 2); + // let workspace_1 = cx + // .update(|cx| cx.windows()[0].downcast::()) + // .unwrap(); + // workspace_1 + // .update(cx, |workspace, cx| { + // assert_eq!( + // workspace + // .worktrees(cx) + // .map(|w| w.read(cx).abs_path()) + // .collect::>(), + // &[Path::new("/root/c").into(), Path::new("/root/d").into()] + // ); + // assert!(workspace.left_dock().read(cx).is_open()); + // assert!(workspace.active_pane().focus_handle(cx).is_focused(cx)); + // }) + // .unwrap(); + // } #[gpui::test] async fn test_window_edit_state(cx: &mut TestAppContext) { From fd2abb7ba132031be9c32a2415e225edd8b7fc68 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 12:31:53 -0500 Subject: [PATCH 14/54] Adjust thickness of tree branches --- crates/collab_ui/src/collab_panel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index ff87bb8b66ca4b04516026c496a726765c9b8d80..ac0925e7b0b6230ab2688d421af77ae5333f3d56 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2408,7 +2408,7 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) -> let rem_size = cx.rem_size(); let line_height = cx.text_style().line_height_in_pixels(rem_size); let width = rem_size * 1.5; - let thickness = px(2.); + let thickness = px(1.); let color = cx.theme().colors().text; canvas(move |bounds, cx| { From 46a99feb972a51f942147a84517cc663871915b7 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 12:58:04 -0500 Subject: [PATCH 15/54] Use correct color for folded diff indicator (#3942) This PR updates the indicator for changes within a fold to use the correct color from the theme: Screenshot 2024-01-08 at 12 52 56 PM Release Notes: - Updated the color of the indicator for a fold containing modified lines. --- crates/editor/src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index e96cb5df0eab53d4bf691c4d13a3e20928856ba5..53a376c2842937a6029cfe6c848b9998153a8d77 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -795,7 +795,7 @@ impl EditorElement { cx.paint_quad(quad( highlight_bounds, Corners::all(1. * line_height), - gpui::yellow(), // todo!("use the right color") + cx.theme().status().modified, Edges::default(), transparent_black(), )); From 4ebff57fa215fcdbda7dcf31a81701c26bd9a7d6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:08:09 +0100 Subject: [PATCH 16/54] Fix flaky open_paths_action test (#3944) I've missed a single run_until_parked call. mb Release Notes: - N/A --- crates/zed/src/zed.rs | 209 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 104 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 61b8d6eaf84332474ff992e5a72a85cbde5b4776..97ef50cabdacd7ab4aebaed9bcedba3236f2c42f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -791,110 +791,111 @@ mod tests { WorkspaceHandle, }; - // #[gpui::test] - // async fn test_open_paths_action(cx: &mut TestAppContext) { - // let app_state = init_test(cx); - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/root", - // json!({ - // "a": { - // "aa": null, - // "ab": null, - // }, - // "b": { - // "ba": null, - // "bb": null, - // }, - // "c": { - // "ca": null, - // "cb": null, - // }, - // "d": { - // "da": null, - // "db": null, - // }, - // }), - // ) - // .await; - - // cx.update(|cx| { - // open_paths( - // &[PathBuf::from("/root/a"), PathBuf::from("/root/b")], - // &app_state, - // None, - // cx, - // ) - // }) - // .await - // .unwrap(); - // assert_eq!(cx.read(|cx| cx.windows().len()), 1); - - // cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) - // .await - // .unwrap(); - // assert_eq!(cx.read(|cx| cx.windows().len()), 1); - // let workspace_1 = cx - // .read(|cx| cx.windows()[0].downcast::()) - // .unwrap(); - // workspace_1 - // .update(cx, |workspace, cx| { - // assert_eq!(workspace.worktrees(cx).count(), 2); - // assert!(workspace.left_dock().read(cx).is_open()); - // assert!(workspace - // .active_pane() - // .read(cx) - // .focus_handle(cx) - // .is_focused(cx)); - // }) - // .unwrap(); - - // cx.update(|cx| { - // open_paths( - // &[PathBuf::from("/root/b"), PathBuf::from("/root/c")], - // &app_state, - // None, - // cx, - // ) - // }) - // .await - // .unwrap(); - // assert_eq!(cx.read(|cx| cx.windows().len()), 2); - - // // Replace existing windows - // let window = cx - // .update(|cx| cx.windows()[0].downcast::()) - // .unwrap(); - // cx.update(|cx| { - // open_paths( - // &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], - // &app_state, - // Some(window), - // cx, - // ) - // }) - // .await - // .unwrap(); - // assert_eq!(cx.read(|cx| cx.windows().len()), 2); - // let workspace_1 = cx - // .update(|cx| cx.windows()[0].downcast::()) - // .unwrap(); - // workspace_1 - // .update(cx, |workspace, cx| { - // assert_eq!( - // workspace - // .worktrees(cx) - // .map(|w| w.read(cx).abs_path()) - // .collect::>(), - // &[Path::new("/root/c").into(), Path::new("/root/d").into()] - // ); - // assert!(workspace.left_dock().read(cx).is_open()); - // assert!(workspace.active_pane().focus_handle(cx).is_focused(cx)); - // }) - // .unwrap(); - // } + #[gpui::test] + async fn test_open_paths_action(cx: &mut TestAppContext) { + let app_state = init_test(cx); + app_state + .fs + .as_fake() + .insert_tree( + "/root", + json!({ + "a": { + "aa": null, + "ab": null, + }, + "b": { + "ba": null, + "bb": null, + }, + "c": { + "ca": null, + "cb": null, + }, + "d": { + "da": null, + "db": null, + }, + }), + ) + .await; + + cx.update(|cx| { + open_paths( + &[PathBuf::from("/root/a"), PathBuf::from("/root/b")], + &app_state, + None, + cx, + ) + }) + .await + .unwrap(); + assert_eq!(cx.read(|cx| cx.windows().len()), 1); + + cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) + .await + .unwrap(); + assert_eq!(cx.read(|cx| cx.windows().len()), 1); + let workspace_1 = cx + .read(|cx| cx.windows()[0].downcast::()) + .unwrap(); + workspace_1 + .update(cx, |workspace, cx| { + assert_eq!(workspace.worktrees(cx).count(), 2); + assert!(workspace.left_dock().read(cx).is_open()); + assert!(workspace + .active_pane() + .read(cx) + .focus_handle(cx) + .is_focused(cx)); + }) + .unwrap(); + + cx.update(|cx| { + open_paths( + &[PathBuf::from("/root/b"), PathBuf::from("/root/c")], + &app_state, + None, + cx, + ) + }) + .await + .unwrap(); + assert_eq!(cx.read(|cx| cx.windows().len()), 2); + + // Replace existing windows + let window = cx + .update(|cx| cx.windows()[0].downcast::()) + .unwrap(); + cx.update(|cx| { + open_paths( + &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], + &app_state, + Some(window), + cx, + ) + }) + .await + .unwrap(); + cx.background_executor.run_until_parked(); + assert_eq!(cx.read(|cx| cx.windows().len()), 2); + let workspace_1 = cx + .update(|cx| cx.windows()[0].downcast::()) + .unwrap(); + workspace_1 + .update(cx, |workspace, cx| { + assert_eq!( + workspace + .worktrees(cx) + .map(|w| w.read(cx).abs_path()) + .collect::>(), + &[Path::new("/root/c").into(), Path::new("/root/d").into()] + ); + assert!(workspace.left_dock().read(cx).is_open()); + assert!(workspace.active_pane().focus_handle(cx).is_focused(cx)); + }) + .unwrap(); + } #[gpui::test] async fn test_window_edit_state(cx: &mut TestAppContext) { From a8c193c7a65307130bdc40cf351b75ffe47476ff Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:08:44 -0500 Subject: [PATCH 17/54] Remove resolved TODO (#3943) This PR removes a resolved TODO in drawing diff hunk status. Release Notes: - N/A --- crates/editor/src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 53a376c2842937a6029cfe6c848b9998153a8d77..c7fbb658a3cf106e1ee7cc6212cf67a18481c84c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -850,7 +850,7 @@ impl EditorElement { cx.paint_quad(quad( highlight_bounds, Corners::all(0.05 * line_height), - color, // todo!("use the right color") + color, Edges::default(), transparent_black(), )); From b57a1f90f491cdbc1862a1de891eee8a674dd51d Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Mon, 8 Jan 2024 13:21:45 -0500 Subject: [PATCH 18/54] Update copilot ui --- crates/copilot_ui/src/sign_in.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/crates/copilot_ui/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs index 029ad149b8d07df22ea7b4bcf97738e2b4631f84..aeaa35784bfabe5ab75fc2b26a59dada83ddb61a 100644 --- a/crates/copilot_ui/src/sign_in.rs +++ b/crates/copilot_ui/src/sign_in.rs @@ -58,6 +58,11 @@ impl CopilotCodeVerification { .map(|item| item.text() == &data.user_code) .unwrap_or(false); h_stack() + .w_full() + .p_1() + .border() + .border_muted(cx) + .rounded_md() .cursor_pointer() .justify_between() .on_mouse_down(gpui::MouseButton::Left, { @@ -67,9 +72,12 @@ impl CopilotCodeVerification { cx.notify(); } }) - .child(Label::new(data.user_code.clone())) - .child(div()) - .child(Label::new(if copied { "Copied!" } else { "Copy" })) + .child(div().flex_1().child(Label::new(data.user_code.clone()))) + .child(div().flex_none().px_1().child(Label::new(if copied { + "Copied!" + } else { + "Copy" + }))) } fn render_prompting_modal( @@ -111,27 +119,28 @@ impl CopilotCodeVerification { } fn render_enabled_modal(cx: &mut ViewContext) -> impl Element { v_stack() - .child(Label::new("Copilot Enabled!")) + .gap_2() + .child(Headline::new("Copilot Enabled!").size(HeadlineSize::Large)) .child(Label::new( "You can update your settings or sign out from the Copilot menu in the status bar.", )) .child( Button::new("copilot-enabled-done-button", "Done") + .full_width() .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))), ) } fn render_unauthorized_modal() -> impl Element { v_stack() + .child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large)) + .child(Label::new( - "Enable Copilot by connecting your existing license.", - )) - .child( - Label::new("You must have an active Copilot license to use it in Zed.") - .color(Color::Warning), - ) + "You can enable Copilot by connecting your existing license once you have subscribed or renewed your subscription.", + ).color(Color::Warning)) .child( Button::new("copilot-subscribe-button", "Subscibe on Github") + .full_width() .on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)), ) } From 72de75dd45ea5a835af1600e9b4047af660eafe8 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:24:56 -0500 Subject: [PATCH 19/54] Adjust spacing of extra call participant count indicator (#3945) This PR adjusts the spacing of the indicator showing the number of extra call participants that appears after the facepile: Screenshot 2024-01-08 at 1 09 39 PM Release Notes: - Added more space between the call participant facepile and the number indicating additional call participants. --- crates/collab_ui/src/collab_panel.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index ac0925e7b0b6230ab2688d421af77ae5333f3d56..5acbca8ffe70f286922f479d0eef381abf9eac56 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2211,8 +2211,12 @@ impl CollabPanel { .map(|user| Avatar::new(user.avatar_uri.clone()).into_any_element()) .take(FACEPILE_LIMIT) .chain(if extra_count > 0 { - // todo!() @nate - this label looks wrong. - Some(Label::new(format!("+{}", extra_count)).into_any_element()) + Some( + div() + .ml_1() + .child(Label::new(format!("+{extra_count}"))) + .into_any_element(), + ) } else { None }) From 5e401e4550f690354a556d75f89afc819042e5ac Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:28:21 -0500 Subject: [PATCH 20/54] Fix layout shift when renaming a channel (#3946) This PR fixes the layout shift that would occur in the channel list when opening a rename editor for a channel. Release Notes: - Fixed layout shift when opening a rename editor for a channel. --- crates/collab_ui/src/collab_panel.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 5acbca8ffe70f286922f479d0eef381abf9eac56..86d0131d70afa9dbf0a9c51d6943dcb1f1ad2f80 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2398,12 +2398,7 @@ impl CollabPanel { { item.child(Label::new(pending_name)) } else { - item.child( - div() - .w_full() - .py_1() // todo!() @nate this is a px off at the default font size. - .child(self.channel_name_editor.clone()), - ) + item.child(self.channel_name_editor.clone()) } } } From e4b1c768959f7328fee9b415560efd9de4bb47a2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:30:18 +0100 Subject: [PATCH 21/54] Display setting documentation in settings.json (#3936) Let this screenshot of settings.json speak for itself: ![image](https://github.com/zed-industries/zed/assets/24362066/fca60383-1788-43f9-803b-00f083394c8a) Release Notes: - Added code completion & on-hover documentation to Zed's settings.json file. --------- Co-authored-by: Marshall Bowers --- Cargo.lock | 1 + assets/settings/default.json | 6 +- crates/assistant/src/assistant_settings.rs | 16 +++++ crates/auto_update/Cargo.toml | 1 + crates/auto_update/src/auto_update.rs | 18 +++-- crates/call/src/call_settings.rs | 1 + crates/client/src/client.rs | 7 ++ crates/collab_ui/src/panel_settings.rs | 9 +++ .../src/project_diagnostics_settings.rs | 4 ++ crates/editor/src/editor_settings.rs | 48 +++++++++++++ crates/journal/src/journal.rs | 7 ++ crates/language/src/language_settings.rs | 68 ++++++++++++++++++ crates/project/src/project_settings.rs | 26 +++++++ .../src/project_panel_settings.rs | 23 ++++++ .../src/semantic_index_settings.rs | 5 ++ crates/terminal/src/terminal_settings.rs | 72 ++++++++++++++++++- crates/vim/src/vim.rs | 3 + crates/welcome/src/base_keymap_setting.rs | 3 + crates/workspace/src/item.rs | 6 ++ crates/workspace/src/workspace_settings.rs | 32 +++++---- 20 files changed, 334 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54e2f483d8a655f3d5e5c6e200b918275f968a34..673a931308accae7931fdd52272bf49067b95ef0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -677,6 +677,7 @@ dependencies = [ "log", "menu", "project", + "schemars", "serde", "serde_derive", "serde_json", diff --git a/assets/settings/default.json b/assets/settings/default.json index 8217f1675a4382d4dd0ab9daa6247391d40b3e4f..bd157c3e6137ccf5e990b94a3085896563931e60 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -76,7 +76,7 @@ // or waits for a `copilot::Toggle` "show_copilot_suggestions": true, // Whether to show tabs and spaces in the editor. - // This setting can take two values: + // This setting can take three values: // // 1. Draw tabs and spaces only for the selected text (default): // "selection" @@ -183,7 +183,7 @@ // Default height when the assistant is docked to the bottom. "default_height": 320, // The default OpenAI model to use when starting new conversations. This - // setting can take two values: + // setting can take three values: // // 1. "gpt-3.5-turbo-0613"" // 2. "gpt-4-0613"" @@ -351,7 +351,7 @@ // } "working_directory": "current_project_directory", // Set the cursor blinking behavior in the terminal. - // May take 4 values: + // May take 3 values: // 1. Never blink the cursor, ignoring the terminal mode // "blinking": "off", // 2. Default the cursor blink to off, but allow the terminal to diff --git a/crates/assistant/src/assistant_settings.rs b/crates/assistant/src/assistant_settings.rs index c0fbc74e9ae9b465abda293aa16e529a96b830ea..b2a9231a5701f287c9ba62f7c8054c1bc52205d1 100644 --- a/crates/assistant/src/assistant_settings.rs +++ b/crates/assistant/src/assistant_settings.rs @@ -57,12 +57,28 @@ pub struct AssistantSettings { pub default_open_ai_model: OpenAIModel, } +/// Assistant panel settings #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct AssistantSettingsContent { + /// Whether to show the assistant panel button in the status bar. + /// + /// Default: true pub button: Option, + /// Where to dock the assistant. + /// + /// Default: right pub dock: Option, + /// Default width in pixels when the assistant is docked to the left or right. + /// + /// Default: 640 pub default_width: Option, + /// Default height in pixels when the assistant is docked to the bottom. + /// + /// Default: 320 pub default_height: Option, + /// The default OpenAI model to use when starting new conversations. + /// + /// Default: gpt-4-1106-preview pub default_open_ai_model: Option, } diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index 884ed2b7a061464e8b649cd77c8d02c0a9d22277..5f0224aa7b2590cc0bfe4dc0d24a88a73206d879 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -22,6 +22,7 @@ anyhow.workspace = true isahc.workspace = true lazy_static.workspace = true log.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index a2a90d4f2f69c94fc87a56ff13d8aa861304699b..06e445e3de96e4e03c560caee3fe4420dfbaed90 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -10,6 +10,7 @@ use gpui::{ }; use isahc::AsyncBody; +use schemars::JsonSchema; use serde::Deserialize; use serde_derive::Serialize; use smol::io::AsyncReadExt; @@ -61,18 +62,27 @@ struct JsonRelease { struct AutoUpdateSetting(bool); +/// Whether or not to automatically check for updates. +/// +/// Default: true +#[derive(Clone, Default, JsonSchema, Deserialize, Serialize)] +#[serde(transparent)] +struct AutoUpdateSettingOverride(Option); + impl Settings for AutoUpdateSetting { const KEY: Option<&'static str> = Some("auto_update"); - type FileContent = Option; + type FileContent = AutoUpdateSettingOverride; fn load( - default_value: &Option, - user_values: &[&Option], + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], _: &mut AppContext, ) -> Result { Ok(Self( - Self::json_merge(default_value, user_values)?.ok_or_else(Self::missing_default)?, + Self::json_merge(default_value, user_values)? + .0 + .ok_or_else(Self::missing_default)?, )) } } diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index 9375feedf07bd805e7952161982f8c022b1ee28f..88c3fe84ce118e011e729e335cf8631ff273e014 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -11,6 +11,7 @@ pub struct CallSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct CallSettingsContent { + /// Whether the microphone should be muted when joining a channel or a call. pub mute_on_join: Option, } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 3eae9d92bb9f8d75cbf7ddb63eab26fdd70ecdc4..0821a8e534d9713b312667dce33eb2c11c93d429 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -352,9 +352,16 @@ pub struct TelemetrySettings { pub metrics: bool, } +/// Control what info is collected by Zed. #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)] pub struct TelemetrySettingsContent { + /// Send debug info like crash reports. + /// + /// Default: true pub diagnostics: Option, + /// Send anonymized usage data like what languages you're using Zed with. + /// + /// Default: true pub metrics: Option, } diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index 250817a803e1088e21ed8965ef9bdbaaa4bf20a5..13fa26a341a57889897e2bf20a1118813448e804 100644 --- a/crates/collab_ui/src/panel_settings.rs +++ b/crates/collab_ui/src/panel_settings.rs @@ -28,8 +28,17 @@ pub struct NotificationPanelSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct PanelSettingsContent { + /// Whether to show the panel button in the status bar. + /// + /// Default: true pub button: Option, + /// Where to dock the panel. + /// + /// Default: left pub dock: Option, + /// Default width of the panel in pixels. + /// + /// Default: 240 pub default_width: Option, } diff --git a/crates/diagnostics/src/project_diagnostics_settings.rs b/crates/diagnostics/src/project_diagnostics_settings.rs index f762d2b1e626f8be1394ccecf2b2d683ca7ea437..d0feeeb3a75a2e395c8f5da12255c0232ffef585 100644 --- a/crates/diagnostics/src/project_diagnostics_settings.rs +++ b/crates/diagnostics/src/project_diagnostics_settings.rs @@ -6,8 +6,12 @@ pub struct ProjectDiagnosticsSettings { pub include_warnings: bool, } +/// Diagnostics configuration. #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct ProjectDiagnosticsSettingsContent { + /// Whether to show warnings or not by default. + /// + /// Default: true include_warnings: Option, } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index fd7e2feea31a07f0b35184087508c76e652b9c26..212ce9fd34d776047fce97f5cc32b63cfc207c65 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -14,11 +14,15 @@ pub struct EditorSettings { pub seed_search_query_from_cursor: SeedQuerySetting, } +/// When to populate a new search's query based on the text under the cursor. #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SeedQuerySetting { + /// Always populate the search query with the word under the cursor. Always, + /// Only populate the search query when there is text selected. Selection, + /// Never populate the search query Never, } @@ -29,31 +33,75 @@ pub struct Scrollbar { pub selections: bool, } +/// When to show the scrollbar in the editor. +/// +/// Default: auto #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum ShowScrollbar { + /// Show the scrollbar if there's important information or + /// follow the system's configured behavior. Auto, + /// Match the system's configured behavior. System, + /// Always show the scrollbar. Always, + /// Never show the scrollbar. Never, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct EditorSettingsContent { + /// Whether the cursor blinks in the editor. + /// + /// Default: true pub cursor_blink: Option, + /// Whether to show the informational hover box when moving the mouse + /// over symbols in the editor. + /// + /// Default: true pub hover_popover_enabled: Option, + /// Whether to pop the completions menu while typing in an editor without + /// explicitly requesting it. + /// + /// Default: true pub show_completions_on_input: Option, + /// Whether to display inline and alongside documentation for items in the + /// completions menu. + /// + /// Default: true pub show_completion_documentation: Option, + /// Whether to use additional LSP queries to format (and amend) the code after + /// every "trigger" symbol input, defined by LSP server capabilities. + /// + /// Default: true pub use_on_type_format: Option, + /// Scrollbar related settings pub scrollbar: Option, + /// Whether the line numbers on editors gutter are relative or not. + /// + /// Default: false pub relative_line_numbers: Option, + /// When to populate a new search's query based on the text under the cursor. + /// + /// Default: always pub seed_search_query_from_cursor: Option, } +/// Scrollbar related settings #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct ScrollbarContent { + /// When to show the scrollbar in the editor. + /// + /// Default: auto pub show: Option, + /// Whether to show git diff indicators in the scrollbar. + /// + /// Default: true pub git_diff: Option, + /// Whether to show buffer search result markers in the scrollbar. + /// + /// Default: true pub selections: Option, } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 2ae74e7f5d5d60c9485c22235633b1a23f952605..1ffab2f3d3e4e6e34514e148b68b4fba48dd2aa5 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -15,9 +15,16 @@ use workspace::{AppState, OpenVisible, Workspace}; actions!(journal, [NewJournalEntry]); +/// Settings specific to journaling #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct JournalSettings { + /// The path of the directory where journal entries are stored. + /// + /// Default: `~` pub path: Option, + /// What format to display the hours in. + /// + /// Default: hour12 pub hour_format: Option, } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 49977f690cd7fbac86e2063f6d569ad671839c73..5359d184d65a9249f8fc82b1eae9c95d71beda6c 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -79,36 +79,90 @@ pub struct AllLanguageSettingsContent { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct LanguageSettingsContent { + /// How many columns a tab should occupy. + /// + /// Default: 4 #[serde(default)] pub tab_size: Option, + /// Whether to indent lines using tab characters, as opposed to multiple + /// spaces. + /// + /// Default: false #[serde(default)] pub hard_tabs: Option, + /// How to soft-wrap long lines of text. + /// + /// Default: none #[serde(default)] pub soft_wrap: Option, + /// The column at which to soft-wrap lines, for buffers where soft-wrap + /// is enabled. + /// + /// Default: 80 #[serde(default)] pub preferred_line_length: Option, + /// Whether to show wrap guides in the editor. Setting this to true will + /// show a guide at the 'preferred_line_length' value if softwrap is set to + /// 'preferred_line_length', and will show any additional guides as specified + /// by the 'wrap_guides' setting. + /// + /// Default: true #[serde(default)] pub show_wrap_guides: Option, + /// Character counts at which to show wrap guides in the editor. + /// + /// Default: [] #[serde(default)] pub wrap_guides: Option>, + /// Whether or not to perform a buffer format before saving. + /// + /// Default: on #[serde(default)] pub format_on_save: Option, + /// Whether or not to remove any trailing whitespace from lines of a buffer + /// before saving it. + /// + /// Default: true #[serde(default)] pub remove_trailing_whitespace_on_save: Option, + /// Whether or not to ensure there's a single newline at the end of a buffer + /// when saving it. + /// + /// Default: true #[serde(default)] pub ensure_final_newline_on_save: Option, + /// How to perform a buffer format. + /// + /// Default: auto #[serde(default)] pub formatter: Option, + /// Zed's Prettier integration settings. + /// If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if + /// project has no other Prettier installed. + /// + /// Default: {} #[serde(default)] pub prettier: Option>, + /// Whether to use language servers to provide code intelligence. + /// + /// Default: true #[serde(default)] pub enable_language_server: Option, + /// Controls whether copilot provides suggestion immediately (true) + /// or waits for a `copilot::Toggle` (false). + /// + /// Default: true #[serde(default)] pub show_copilot_suggestions: Option, + /// Whether to show tabs and spaces in the editor. #[serde(default)] pub show_whitespaces: Option, + /// Whether to start a new line with a comment when a previous line is a comment as well. + /// + /// Default: true #[serde(default)] pub extend_comment_on_newline: Option, + /// Inlay hint related settings. #[serde(default)] pub inlay_hints: Option, } @@ -128,8 +182,11 @@ pub struct FeaturesContent { #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SoftWrap { + /// Do not soft wrap. None, + /// Soft wrap lines that overflow the editor EditorWidth, + /// Soft wrap lines at the preferred line length PreferredLineLength, } @@ -148,18 +205,26 @@ pub enum FormatOnSave { #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ShowWhitespaceSetting { + /// Draw tabs and spaces only for the selected text. Selection, + /// Do not draw any tabs or spaces None, + /// Draw all invisible symbols All, } #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Formatter { + /// Format files using Zed's Prettier integration (if applicable), + /// or falling back to formatting via language server. #[default] Auto, + /// Format code using the current language server. LanguageServer, + /// Format code using Zed's Prettier integration. Prettier, + /// Format code using an external command. External { command: Arc, arguments: Arc<[String]>, @@ -168,6 +233,9 @@ pub enum Formatter { #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct InlayHintSettings { + /// Global switch to toggle hints on and off. + /// + /// Default: false #[serde(default)] pub enabled: bool, #[serde(default = "default_true")] diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 2a8df47e67a5b7ad7e540e581b055da546c3f7cf..925109ac964044a2e93a4a1a7ba23b493a2434ed 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -7,16 +7,40 @@ use std::sync::Arc; #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct ProjectSettings { + /// Configuration for language servers. + /// + /// The following settings can be overriden for specific language servers: + /// - initialization_options + /// To override settings for a language, add an entry for that language server's + /// name to the lsp value. + /// Default: null #[serde(default)] pub lsp: HashMap, LspSettings>, + + /// Configuration for Git-related features #[serde(default)] pub git: GitSettings, + /// Completely ignore files matching globs from `file_scan_exclusions` + /// + /// Default: [ + /// "**/.git", + /// "**/.svn", + /// "**/.hg", + /// "**/CVS", + /// "**/.DS_Store", + /// "**/Thumbs.db", + /// "**/.classpath", + /// "**/.settings" + /// ] #[serde(default)] pub file_scan_exclusions: Option>, } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct GitSettings { + /// Whether or not to show the git gutter. + /// + /// Default: tracked_files pub git_gutter: Option, pub gutter_debounce: Option, } @@ -24,8 +48,10 @@ pub struct GitSettings { #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum GitGutterSetting { + /// Show git gutter in tracked files. #[default] TrackedFiles, + /// Hide git gutter Hide, } diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index b9a87a1a03cf3ac2164fb4ad5db6144f0a3fc6de..5285684891e2b2945424b8b99bb70c47652963ac 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -24,12 +24,35 @@ pub struct ProjectPanelSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct ProjectPanelSettingsContent { + /// Customise default width (in pixels) taken by project panel + /// + /// Default: 240 pub default_width: Option, + /// The position of project panel + /// + /// Default: left pub dock: Option, + /// Whether to show file icons in the project panel. + /// + /// Default: true pub file_icons: Option, + /// Whether to show folder icons or chevrons for directories in the project panel. + /// + /// Default: true pub folder_icons: Option, + /// Whether to show the git status in the project panel. + /// + /// Default: true pub git_status: Option, + /// Amount of indentation (in pixels) for nested items. + /// + /// Default: 20 pub indent_size: Option, + /// Whether to reveal it in the project panel automatically, + /// when a corresponding project entry becomes active. + /// Gitignored entries are never auto revealed. + /// + /// Default: true pub auto_reveal_entries: Option, } diff --git a/crates/semantic_index/src/semantic_index_settings.rs b/crates/semantic_index/src/semantic_index_settings.rs index 306a38fa9c2ec52f5a69d27898cc9fccc1af956c..73fd49c8f5f61dad875f963586a87101371f620e 100644 --- a/crates/semantic_index/src/semantic_index_settings.rs +++ b/crates/semantic_index/src/semantic_index_settings.rs @@ -8,8 +8,13 @@ pub struct SemanticIndexSettings { pub enabled: bool, } +/// Configuration of semantic index, an alternate search engine available in +/// project search. #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct SemanticIndexSettingsContent { + /// Whether or not to display the Semantic mode in project search. + /// + /// Default: true pub enabled: Option, } diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index f63b575bf214871e5391aefe9889fc5b69da2fba..14cff3b5a690a6bc370579604305ff5a1217a351 100644 --- a/crates/terminal/src/terminal_settings.rs +++ b/crates/terminal/src/terminal_settings.rs @@ -36,6 +36,9 @@ pub enum VenvSettings { #[default] Off, On { + /// Default directories to search for virtual environments, relative + /// to the current working directory. We recommend overriding this + /// in your project's settings, rather than globally. activate_script: Option, directories: Option>, }, @@ -73,20 +76,68 @@ pub enum ActivateScript { #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct TerminalSettingsContent { + /// What shell to use when opening a terminal. + /// + /// Default: system pub shell: Option, + /// What working directory to use when launching the terminal + /// + /// Default: current_project_directory pub working_directory: Option, + /// Set the terminal's font size. + /// + /// If this option is not included, + /// the terminal will default to matching the buffer's font size. pub font_size: Option, + /// Set the terminal's font family. + /// + /// If this option is not included, + /// the terminal will default to matching the buffer's font family. pub font_family: Option, + /// Set the terminal's line height. + /// + /// Default: comfortable pub line_height: Option, pub font_features: Option, + /// Any key-value pairs added to this list will be added to the terminal's + /// environment. Use `:` to separate multiple values. + /// + /// Default: {} pub env: Option>, + /// Set the cursor blinking behavior in the terminal. + /// + /// Default: terminal_controlled pub blinking: Option, + /// Set whether Alternate Scroll mode (code: ?1007) is active by default. + /// Alternate Scroll mode converts mouse scroll events into up / down key + /// presses when in the alternate screen (e.g. when running applications + /// like vim or less). The terminal can still set and unset this mode. + /// + /// Default: off pub alternate_scroll: Option, + /// Set whether the option key behaves as the meta key. + /// + /// Default: false pub option_as_meta: Option, + /// Whether or not selecting text in the terminal will automatically + /// copy to the system clipboard. + /// + /// Default: false pub copy_on_select: Option, pub dock: Option, + /// Default width when the terminal is docked to the left or right. + /// + /// Default: 640 pub default_width: Option, + /// Default height when the terminal is docked to the bottom. + /// + /// Default: 320 pub default_height: Option, + /// Activate the python virtual environment, if one is found, in the + /// terminal's working directory (as resolved by the working_directory + /// setting). Set this to "off" to disable this behavior. + /// + /// Default: on pub detect_venv: Option, } @@ -107,9 +158,13 @@ impl settings::Settings for TerminalSettings { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] #[serde(rename_all = "snake_case")] pub enum TerminalLineHeight { + /// Use a line height that's comfortable for reading, 1.618 #[default] Comfortable, + /// Use a standard line height, 1.3. This option is useful for TUIs, + /// particularly if they use box characters Standard, + /// Use a custom line height. Custom(f32), } @@ -127,17 +182,25 @@ impl TerminalLineHeight { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum TerminalBlink { + /// Never blink the cursor, ignoring the terminal mode. Off, + /// Default the cursor blink to off, but allow the terminal to + /// set blinking. TerminalControlled, + /// Always blink the cursor, ignoring the terminal mode. On, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Shell { + /// Use the system's default terminal configuration in /etc/passwd System, Program(String), - WithArguments { program: String, args: Vec }, + WithArguments { + program: String, + args: Vec, + }, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -150,8 +213,15 @@ pub enum AlternateScroll { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum WorkingDirectory { + /// Use the current file's project directory. Will Fallback to the + /// first project directory strategy if unsuccessful. CurrentProjectDirectory, + /// Use the first project in this workspace's directory. FirstProjectDirectory, + /// Always use this platform's home directory (if it can be found). AlwaysHome, + /// Slways use a specific directory. This value will be shell expanded. + /// If this path is not a valid directory the terminal will default to + /// this platform's home directory (if it can be found). Always { directory: String }, } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 62205630a153e886af9021f1bb45671dd496b7b7..3579bf36fe29a7276496a1bbefd95dabf6cc8bc2 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -33,6 +33,9 @@ use workspace::{self, Workspace}; use crate::state::ReplayableAction; +/// Whether or not to enable Vim mode (work in progress). +/// +/// Default: false pub struct VimModeSetting(pub bool); #[derive(Clone, Deserialize, PartialEq)] diff --git a/crates/welcome/src/base_keymap_setting.rs b/crates/welcome/src/base_keymap_setting.rs index 411caa820e34e2cc080c160c39f4053161901e92..e05a16c350d4de2a7f1c0ddf56b7b581ef7d6b17 100644 --- a/crates/welcome/src/base_keymap_setting.rs +++ b/crates/welcome/src/base_keymap_setting.rs @@ -4,6 +4,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::Settings; +/// Base key bindings scheme. Base keymaps can be overriden with user keymaps. +/// +/// Default: VSCode #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] pub enum BaseKeymap { #[default] diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 45f6141df2f172ccad176c48bc1f83eab6174c3e..c629edc696b87e0552ca05956e2a1c5cf3b5e6b0 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -60,7 +60,13 @@ impl ClosePosition { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct ItemSettingsContent { + /// Whether to show the Git file status on a tab item. + /// + /// Default: true git_status: Option, + /// Position of the close button in a tab. + /// + /// Default: right close_position: Option, } diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index f3882a9dbdd53b9935a179d86e7ed698eb026bba..4a922b85c2b42034d5e6c05a755c6a5ee43c4b8c 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -12,35 +12,39 @@ pub struct WorkspaceSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct WorkspaceSettingsContent { + /// Scale by which to zoom the active pane. + /// When set to 1.0, the active pane has the same size as others, + /// but when set to a larger value, the active pane takes up more space. + /// + /// Default: `1.0` pub active_pane_magnification: Option, + /// Whether or not to prompt the user to confirm before closing the application. + /// + /// Default: false pub confirm_quit: Option, + /// Whether or not to show the call status icon in the status bar. + /// + /// Default: true pub show_call_status_icon: Option, + /// When to automatically save edited buffers. + /// + /// Default: off pub autosave: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AutosaveSetting { + /// Disable autosave. Off, + /// Save after inactivity period of `milliseconds`. AfterDelay { milliseconds: u64 }, + /// Autosave when focus changes. OnFocusChange, + /// Autosave when the active window changes. OnWindowChange, } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct GitSettings { - pub git_gutter: Option, - pub gutter_debounce: Option, -} - -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum GitGutterSetting { - #[default] - TrackedFiles, - Hide, -} - impl Settings for WorkspaceSettings { const KEY: Option<&'static str> = None; From 00fc22a461b838dd3de057e7b86ee84d7aceb575 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:33:41 -0500 Subject: [PATCH 22/54] terminal: Clean up doc comment for `rgb_for_index` (#3947) This PR cleans up the doc comment for the `rgb_for_index` function. Release Notes: - N/A --- crates/terminal/src/terminal.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index e1605eb4fb4e35b16c7e08bee6107537462cc2ed..fa8112bac7d3cc0b33cab2d4cbfdf633cd9ee610 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1459,14 +1459,16 @@ pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla { } } -///Generates the rgb channels in [0, 5] for a given index into the 6x6x6 ANSI color cube -///See: [8 bit ansi color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit). +/// Generates the RGB channels in [0, 5] for a given index into the 6x6x6 ANSI color cube. +/// See: [8 bit ANSI color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit). /// -///Wikipedia gives a formula for calculating the index for a given color: +/// Wikipedia gives a formula for calculating the index for a given color: /// -///index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) +/// ``` +/// index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) +/// ``` /// -///This function does the reverse, calculating the r, g, and b components from a given index. +/// This function does the reverse, calculating the `r`, `g`, and `b` components from a given index. fn rgb_for_index(i: &u8) -> (u8, u8, u8) { debug_assert!((&16..=&231).contains(&i)); let i = i - 16; From 9b06c66e00f19302be25d2dcca1def022819609c Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:39:56 -0500 Subject: [PATCH 23/54] terminal: Remove resolved TODO (#3948) This PR removes a resolved TODO in the `terminal` crate. Release Notes: - N/A --- crates/terminal_view/src/terminal_view.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 8f5044e49e01e5ed2244581f24d1663f8ee8c73a..f4fb6105cb8bcf476a12d68d29aadb0d942a22dc 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -2,8 +2,6 @@ mod persistence; pub mod terminal_element; pub mod terminal_panel; -// todo!() -// use crate::terminal_element::TerminalElement; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{ div, impl_actions, overlay, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, From 5c28c688faff2a92fd21b2d517f58f83116b8208 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 13:51:18 -0500 Subject: [PATCH 24/54] theme: Removed commented-out `ActiveTheme` impl (#3950) This PR removes a commented-out implementation for the `ActiveTheme` trait on the `WindowContext`. We don't need this implementation as we can go through the `AppContext`'s implementation. Release Notes: - N/A --- crates/theme/src/theme.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c526a381b72f29d0cb0dc9e9ff5724ba38a69c13..93253b95e7358962707ef0fc22fbe01a01f7cd56 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -73,13 +73,6 @@ impl ActiveTheme for AppContext { } } -// todo!() -// impl<'a> ActiveTheme for WindowContext<'a> { -// fn theme(&self) -> &Arc { -// &ThemeSettings::get_global(self.app()).active_theme -// } -// } - pub struct ThemeFamily { pub id: String, pub name: SharedString, From a3bc48261e49d7cb483c5371fade67cc568ebc9d Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 12:23:40 -0700 Subject: [PATCH 25/54] First pass of real access control Co-Authored-By: Max --- crates/collab/src/db/ids.rs | 8 ++ crates/collab/src/db/queries/projects.rs | 38 ++++++++ crates/collab/src/rpc.rs | 86 ++++++++++++------- .../collab/src/tests/channel_guest_tests.rs | 10 ++- 4 files changed, 112 insertions(+), 30 deletions(-) diff --git a/crates/collab/src/db/ids.rs b/crates/collab/src/db/ids.rs index 9bb766147f5b0b2084b76665262962179a62e6eb..2e2218c4d9da181719cf6404245a4d84d4285dab 100644 --- a/crates/collab/src/db/ids.rs +++ b/crates/collab/src/db/ids.rs @@ -140,6 +140,14 @@ impl ChannelRole { Guest | Banned => false, } } + + pub fn can_edit_projects(&self) -> bool { + use ChannelRole::*; + match self { + Admin | Member => true, + Guest | Banned => false, + } + } } impl From for ChannelRole { diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 5b8d54f8d36a746b2c09a77cbf9e75b77b557543..ca59c851e738865c6ffc50c970490fa3e2a93d1f 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -777,6 +777,44 @@ impl Database { .await } + pub async fn host_for_mutating_project_request( + &self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + let current_participant = room_participant::Entity::find() + .filter(room_participant::Column::RoomId.eq(room_id)) + .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.id)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such room"))?; + + if !current_participant + .role + .unwrap_or(ChannelRole::Guest) + .can_edit_projects() + { + Err(anyhow!("not authorized to edit projects"))?; + } + + let host = project_collaborator::Entity::find() + .filter( + project_collaborator::Column::ProjectId + .eq(project_id) + .and(project_collaborator::Column::IsHost.eq(true)), + ) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("failed to read project host"))?; + + Ok(host.connection()) + }) + .await + .map(|guard| guard.into_inner()) + } + pub async fn project_collaborators( &self, project_id: ProjectId, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 835b48809da94dc60cd872d473e564a7456da81e..68774c22e6ec67782c76c6878dad974dfd7db984 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -217,39 +217,43 @@ impl Server { .add_message_handler(update_diagnostic_summary) .add_message_handler(update_worktree_settings) .add_message_handler(refresh_inlay_hints) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler( + forward_mutating_project_request::, + ) + .add_request_handler( + forward_mutating_project_request::, + ) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) .add_message_handler(create_buffer_for_peer) .add_request_handler(update_buffer) .add_message_handler(update_buffer_file) .add_message_handler(buffer_reloaded) .add_message_handler(buffer_saved) - .add_request_handler(forward_project_request::) .add_request_handler(get_users) .add_request_handler(fuzzy_search_users) .add_request_handler(request_contact) @@ -1741,7 +1745,7 @@ async fn update_language_server( Ok(()) } -async fn forward_project_request( +async fn forward_read_only_project_request( request: T, response: Response, session: Session, @@ -1772,6 +1776,30 @@ where Ok(()) } +async fn forward_mutating_project_request( + request: T, + response: Response, + session: Session, +) -> Result<()> +where + T: EntityMessage + RequestMessage, +{ + let project_id = ProjectId::from_proto(request.remote_entity_id()); + let host_connection_id = session + .db() + .await + .host_for_mutating_project_request(project_id, session.connection_id) + .await?; + + let payload = session + .peer + .forward_request(session.connection_id, host_connection_id, request) + .await?; + + response.send(payload)?; + Ok(()) +} + async fn create_buffer_for_peer( request: proto::CreateBufferForPeer, session: Session, diff --git a/crates/collab/src/tests/channel_guest_tests.rs b/crates/collab/src/tests/channel_guest_tests.rs index e2051c44a038ced0724b616ac4e3b4cd851b4508..32cc074ec96a11e3806a256eedd8f74191689ace 100644 --- a/crates/collab/src/tests/channel_guest_tests.rs +++ b/crates/collab/src/tests/channel_guest_tests.rs @@ -82,5 +82,13 @@ async fn test_channel_guests( project_b.read_with(cx_b, |project, _| project.remote_id()), Some(project_id), ); - assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())) + assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); + + assert!(project_b + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees().next().unwrap().read(cx).id(); + project.create_entry((worktree_id, "b.txt"), false, cx) + }) + .await + .is_err()) } From 8214a7f6567a61216479f9ab05e48bd6e2af21ef Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 14:40:48 -0500 Subject: [PATCH 26/54] Fix inconsistent sizing between the buffer search and the project search (#3951) This PR fixes the inconsistent sizing between the buffer search and the project search when both elements have both a primary and secondary item shown in the toolbar. Release Notes: - Fixed some inconsistent sizing in the buffer search and project search when both have two rows of tools in the toolbar. --- crates/breadcrumbs/src/breadcrumbs.rs | 5 ++++- crates/quick_action_bar/src/quick_action_bar.rs | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index 2e4306f0bc2b3694b9b4312f7a8458ed96ba44d7..e41c0c06b180cab749f7e34bb1199bd28709201b 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -67,7 +67,10 @@ impl Render for Breadcrumbs { }) .tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)), ), - None => element.child(breadcrumbs_stack), + None => element + // Match the height of the `ButtonLike` in the other arm. + .h(rems(22. / 16.)) + .child(breadcrumbs_stack), } } } diff --git a/crates/quick_action_bar/src/quick_action_bar.rs b/crates/quick_action_bar/src/quick_action_bar.rs index d8c42589d63bea9775145f6efb211d64077d3af0..b40794c2fad4cfe4d02be825e1ec49be82e29ff4 100644 --- a/crates/quick_action_bar/src/quick_action_bar.rs +++ b/crates/quick_action_bar/src/quick_action_bar.rs @@ -95,7 +95,6 @@ impl Render for QuickActionBar { h_stack() .id("quick action bar") - .p_1() .gap_2() .children(inlay_hints_button) .children(search_button) From dd730549df0794de8945afcaf3c28fd9c4476257 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:46:35 +0100 Subject: [PATCH 27/54] chore: Fix spurious rebuilds of Zed library and binary (#3952) Currently when one runs `cargo build` twice without changing anything, zed lib and binary are gonna get rebuilt every time. I checked out cargo logs and it turns out we were querying wrong path to `.git/logs/HEAD` in our build.rs, causing it to rerun on every build and thus marking zed lib as dirty. See logs (`CARGO_LOG=cargo::core::compiler::fingerprint=trace cargo build`): ``` 0.501173792s INFO cargo::core::compiler::fingerprint: fingerprint dirty for zed v0.120.0 (/Users/hiro/Projects/zed/crates/zed)/RunCustomBuild/TargetInner { ..: custom_build_target("build-script-build", "/Users/hiro/Projects/zed/crates/zed/build.rs", Edition2021) } 0.501180417s INFO cargo::core::compiler::fingerprint: dirty: FsStatusOutdated(StaleItem(MissingFile("/Users/hiro/Projects/zed/crates/zed/.git/logs/HEAD"))) ``` The path to .git directory is relative to crates/zed and not to the workspace's root. /cc @maxbrunsfeld Release Notes: - N/A --- crates/zed/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed/build.rs b/crates/zed/build.rs index 08608d0c6a07b3a823c082a4f41ee7f34cc7f3f7..0b13f5bd2f5784e2d17c4b4ff756a5b3a0e221ec 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -22,7 +22,7 @@ fn main() { println!("cargo:rustc-link-arg=-Wl,-ObjC"); // Populate git sha environment variable if git is available - println!("cargo:rerun-if-changed=.git/logs/HEAD"); + println!("cargo:rerun-if-changed=../../.git/logs/HEAD"); if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { if output.status.success() { let git_sha = String::from_utf8_lossy(&output.stdout); From 2ca17bd03ea81e4afe386618bef50eecff43046e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 8 Jan 2024 21:52:03 +0200 Subject: [PATCH 28/54] Clean up outline selections when its modal gets closed --- crates/outline/src/outline.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 75d1a0935734c4d40226554202fb24c3a5ce2562..a661a693b1f5733dd27218f27ad502ea0d763c78 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -54,7 +54,13 @@ impl FocusableView for OutlineView { } impl EventEmitter for OutlineView {} -impl ModalView for OutlineView {} +impl ModalView for OutlineView { + fn on_before_dismiss(&mut self, cx: &mut ViewContext) -> bool { + self.picker + .update(cx, |picker, cx| picker.delegate.restore_active_editor(cx)); + true + } +} impl Render for OutlineView { fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { From 18b31f1552db6f7a25ae8011fe5ffd467095cd79 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 8 Jan 2024 12:04:59 -0800 Subject: [PATCH 29/54] Check user is host for host-broadcasted project messages --- crates/collab/src/db/queries/projects.rs | 28 ++++++++ crates/collab/src/rpc.rs | 92 +++++------------------- crates/rpc/src/macros.rs | 4 +- crates/rpc/src/proto.rs | 5 +- 4 files changed, 51 insertions(+), 78 deletions(-) diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index ca59c851e738865c6ffc50c970490fa3e2a93d1f..04c77c80771b29a6be3d1603898b681da3e79569 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -777,6 +777,34 @@ impl Database { .await } + pub async fn check_user_is_project_host( + &self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result<()> { + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + project_collaborator::Entity::find() + .filter( + Condition::all() + .add(project_collaborator::Column::ProjectId.eq(project_id)) + .add(project_collaborator::Column::IsHost.eq(true)) + .add(project_collaborator::Column::ConnectionId.eq(connection_id.id)) + .add( + project_collaborator::Column::ConnectionServerId + .eq(connection_id.owner_id), + ), + ) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("failed to read project host"))?; + + Ok(()) + }) + .await + .map(|guard| guard.into_inner()) + } + pub async fn host_for_mutating_project_request( &self, project_id: ProjectId, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 68774c22e6ec67782c76c6878dad974dfd7db984..572670d78f4dca73cff7e57b8d0f0b030d9bdd11 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -42,7 +42,7 @@ use prometheus::{register_int_gauge, IntGauge}; use rpc::{ proto::{ self, Ack, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, LiveKitConnectionInfo, - RequestMessage, UpdateChannelBufferCollaborators, + RequestMessage, ShareProject, UpdateChannelBufferCollaborators, }, Connection, ConnectionId, Peer, Receipt, TypedEnvelope, }; @@ -216,7 +216,6 @@ impl Server { .add_message_handler(update_language_server) .add_message_handler(update_diagnostic_summary) .add_message_handler(update_worktree_settings) - .add_message_handler(refresh_inlay_hints) .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) @@ -251,9 +250,11 @@ impl Server { .add_request_handler(forward_mutating_project_request::) .add_message_handler(create_buffer_for_peer) .add_request_handler(update_buffer) - .add_message_handler(update_buffer_file) - .add_message_handler(buffer_reloaded) - .add_message_handler(buffer_saved) + .add_message_handler(broadcast_project_message_from_host::) + .add_message_handler(broadcast_project_message_from_host::) + .add_message_handler(broadcast_project_message_from_host::) + .add_message_handler(broadcast_project_message_from_host::) + .add_message_handler(broadcast_project_message_from_host::) .add_request_handler(get_users) .add_request_handler(fuzzy_search_users) .add_request_handler(request_contact) @@ -285,7 +286,6 @@ impl Server { .add_request_handler(follow) .add_message_handler(unfollow) .add_message_handler(update_followers) - .add_message_handler(update_diff_base) .add_request_handler(get_private_user_info) .add_message_handler(acknowledge_channel_message) .add_message_handler(acknowledge_buffer_version); @@ -1697,10 +1697,6 @@ async fn update_worktree_settings( Ok(()) } -async fn refresh_inlay_hints(request: proto::RefreshInlayHints, session: Session) -> Result<()> { - broadcast_project_message(request.project_id, request, session).await -} - async fn start_language_server( request: proto::StartLanguageServer, session: Session, @@ -1804,6 +1800,14 @@ async fn create_buffer_for_peer( request: proto::CreateBufferForPeer, session: Session, ) -> Result<()> { + session + .db() + .await + .check_user_is_project_host( + ProjectId::from_proto(request.project_id), + session.connection_id, + ) + .await?; let peer_id = request.peer_id.ok_or_else(|| anyhow!("invalid peer id"))?; session .peer @@ -1856,60 +1860,17 @@ async fn update_buffer( Ok(()) } -async fn update_buffer_file(request: proto::UpdateBufferFile, session: Session) -> Result<()> { - let project_id = ProjectId::from_proto(request.project_id); - let project_connection_ids = session - .db() - .await - .project_connection_ids(project_id, session.connection_id) - .await?; - - broadcast( - Some(session.connection_id), - project_connection_ids.iter().copied(), - |connection_id| { - session - .peer - .forward_send(session.connection_id, connection_id, request.clone()) - }, - ); - Ok(()) -} - -async fn buffer_reloaded(request: proto::BufferReloaded, session: Session) -> Result<()> { - let project_id = ProjectId::from_proto(request.project_id); - let project_connection_ids = session - .db() - .await - .project_connection_ids(project_id, session.connection_id) - .await?; - broadcast( - Some(session.connection_id), - project_connection_ids.iter().copied(), - |connection_id| { - session - .peer - .forward_send(session.connection_id, connection_id, request.clone()) - }, - ); - Ok(()) -} - -async fn buffer_saved(request: proto::BufferSaved, session: Session) -> Result<()> { - broadcast_project_message(request.project_id, request, session).await -} - -async fn broadcast_project_message( - project_id: u64, +async fn broadcast_project_message_from_host>( request: T, session: Session, ) -> Result<()> { - let project_id = ProjectId::from_proto(project_id); + let project_id = ProjectId::from_proto(request.remote_entity_id()); let project_connection_ids = session .db() .await .project_connection_ids(project_id, session.connection_id) .await?; + broadcast( Some(session.connection_id), project_connection_ids.iter().copied(), @@ -3138,25 +3099,6 @@ async fn mark_notification_as_read( Ok(()) } -async fn update_diff_base(request: proto::UpdateDiffBase, session: Session) -> Result<()> { - let project_id = ProjectId::from_proto(request.project_id); - let project_connection_ids = session - .db() - .await - .project_connection_ids(project_id, session.connection_id) - .await?; - broadcast( - Some(session.connection_id), - project_connection_ids.iter().copied(), - |connection_id| { - session - .peer - .forward_send(session.connection_id, connection_id, request.clone()) - }, - ); - Ok(()) -} - async fn get_private_user_info( _request: proto::GetPrivateUserInfo, response: Response, diff --git a/crates/rpc/src/macros.rs b/crates/rpc/src/macros.rs index 89e605540da1157f5530ad7236b23358dc127c1a..85e2b0cf879608aec655d8eb663a73baf65fe7f0 100644 --- a/crates/rpc/src/macros.rs +++ b/crates/rpc/src/macros.rs @@ -60,8 +60,10 @@ macro_rules! request_messages { #[macro_export] macro_rules! entity_messages { - ($id_field:ident, $($name:ident),* $(,)?) => { + ({$id_field:ident, $entity_type:ty}, $($name:ident),* $(,)?) => { $(impl EntityMessage for $name { + type Entity = $entity_type; + fn remote_entity_id(&self) -> u64 { self.$id_field } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 336c252630ea51ea9d11a0658fc59d0cb09b214f..25b8b00dae99fe3e4c11677e480c6c48fa9f08c8 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -31,6 +31,7 @@ pub trait EnvelopedMessage: Clone + Debug + Serialize + Sized + Send + Sync + 's } pub trait EntityMessage: EnvelopedMessage { + type Entity; fn remote_entity_id(&self) -> u64; } @@ -369,7 +370,7 @@ request_messages!( ); entity_messages!( - project_id, + {project_id, ShareProject}, AddProjectCollaborator, ApplyCodeAction, ApplyCompletionAdditionalEdits, @@ -422,7 +423,7 @@ entity_messages!( ); entity_messages!( - channel_id, + {channel_id, Channel}, ChannelMessageSent, RemoveChannelMessage, UpdateChannelBuffer, From 35ce34c6765963c4bd7dd0ae9b5d43e1f2651dcb Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 8 Jan 2024 22:12:40 +0200 Subject: [PATCH 30/54] Show context menu on project panel empty space right click --- crates/project_panel/src/project_panel.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index a5fb8671f7fd4b6cdd9d8dff0b7dfa6801dcb76d..ee016f399e0b2979423f5d5dc625d616013526d3 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1515,6 +1515,16 @@ impl Render for ProjectPanel { el.on_action(cx.listener(Self::reveal_in_finder)) .on_action(cx.listener(Self::open_in_terminal)) }) + .on_mouse_down( + MouseButton::Right, + cx.listener(move |this, event: &MouseDownEvent, cx| { + // When deploying the context menu anywhere below the last project entry, + // act as if the user clicked the root of the last worktree. + if let Some(entry_id) = this.last_worktree_root_id { + this.deploy_context_menu(event.position, entry_id, cx); + } + }), + ) .track_focus(&self.focus_handle) .child( uniform_list( From 604fcd8f1d2c9a720176192f982064e4f7df4aeb Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 13:46:08 -0700 Subject: [PATCH 31/54] No .. paths... --- crates/project/src/project.rs | 9 +++- crates/project/src/project_tests.rs | 75 +++++++++++++++++++++++++++++ crates/project/src/worktree.rs | 32 +++++++----- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index fb3eae1945f13fb1403b89b7556d548caaa76fff..584638a47a355495d2ffdf9f7da4712c23e30c4a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -6581,7 +6581,14 @@ impl Project { let removed = *change == PathChange::Removed; let abs_path = worktree.absolutize(path); settings_contents.push(async move { - (settings_dir, (!removed).then_some(fs.load(&abs_path).await)) + ( + settings_dir, + if removed { + None + } else { + Some(async move { fs.load(&abs_path?).await }.await) + }, + ) }); } } diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 8f41c75fb4de0415089c1ce66c30cb93278c079c..5c3c21fd08c988a0987a1e1acf0a3c8eb28e38d7 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -4278,6 +4278,81 @@ fn test_glob_literal_prefix() { assert_eq!(glob_literal_prefix("foo/bar/baz.js"), "foo/bar/baz.js"); } +#[gpui::test] +async fn test_create_entry(cx: &mut gpui::TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.executor().clone()); + fs.insert_tree( + "/one/two", + json!({ + "three": { + "a.txt": "", + "four": {} + }, + "c.rs": "" + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/one/two/three".as_ref()], cx).await; + project + .update(cx, |project, cx| { + let id = project.worktrees().next().unwrap().read(cx).id(); + project.create_entry((id, "b.."), true, cx) + }) + .unwrap() + .await + .unwrap(); + + // Can't create paths outside the project + let result = project + .update(cx, |project, cx| { + let id = project.worktrees().next().unwrap().read(cx).id(); + project.create_entry((id, "../../boop"), true, cx) + }) + .await; + assert!(result.is_err()); + + // Can't create paths with '..' + let result = project + .update(cx, |project, cx| { + let id = project.worktrees().next().unwrap().read(cx).id(); + project.create_entry((id, "four/../beep"), true, cx) + }) + .await; + assert!(result.is_err()); + + assert_eq!( + fs.paths(true), + vec![ + PathBuf::from("/"), + PathBuf::from("/one"), + PathBuf::from("/one/two"), + PathBuf::from("/one/two/c.rs"), + PathBuf::from("/one/two/three"), + PathBuf::from("/one/two/three/a.txt"), + PathBuf::from("/one/two/three/b.."), + PathBuf::from("/one/two/three/four"), + ] + ); + + // ************************************ + // Note: unsure if this is the best fix for the integration failure, but assuming we want + // to keep that behavior, then this test should cover it + // ************************************ + + // But we can open buffers with '..' + let result = project + .update(cx, |project, cx| { + let id = project.worktrees().next().unwrap().read(cx).id(); + project.open_buffer((id, "../c.rs"), cx) + }) + .await; + + assert!(dbg!(result).is_ok()) +} + async fn search( project: &Model, query: SearchQuery, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index ae0c074188274b95fbed3b078f68378ce715e570..461ea303b3d42cedb577d53f22c9e1de75a14b6a 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -965,6 +965,7 @@ impl LocalWorktree { let entry = self.refresh_entry(path.clone(), None, cx); cx.spawn(|this, mut cx| async move { + let abs_path = abs_path?; let text = fs.load(&abs_path).await?; let mut index_task = None; let snapshot = this.update(&mut cx, |this, _| this.as_local().unwrap().snapshot())?; @@ -1050,6 +1051,7 @@ impl LocalWorktree { cx.spawn(move |this, mut cx| async move { let entry = save.await?; + let abs_path = abs_path?; let this = this.upgrade().context("worktree dropped")?; let (entry_id, mtime, path) = match entry { @@ -1139,9 +1141,9 @@ impl LocalWorktree { let fs = self.fs.clone(); let write = cx.background_executor().spawn(async move { if is_dir { - fs.create_dir(&abs_path).await + fs.create_dir(&abs_path?).await } else { - fs.save(&abs_path, &Default::default(), Default::default()) + fs.save(&abs_path?, &Default::default(), Default::default()) .await } }); @@ -1188,7 +1190,7 @@ impl LocalWorktree { let fs = self.fs.clone(); let write = cx .background_executor() - .spawn(async move { fs.save(&abs_path, &text, line_ending).await }); + .spawn(async move { fs.save(&abs_path?, &text, line_ending).await }); cx.spawn(|this, mut cx| async move { write.await?; @@ -1210,10 +1212,10 @@ impl LocalWorktree { let delete = cx.background_executor().spawn(async move { if entry.is_file() { - fs.remove_file(&abs_path, Default::default()).await?; + fs.remove_file(&abs_path?, Default::default()).await?; } else { fs.remove_dir( - &abs_path, + &abs_path?, RemoveOptions { recursive: true, ignore_if_not_exists: false, @@ -1252,7 +1254,7 @@ impl LocalWorktree { let abs_new_path = self.absolutize(&new_path); let fs = self.fs.clone(); let rename = cx.background_executor().spawn(async move { - fs.rename(&abs_old_path, &abs_new_path, Default::default()) + fs.rename(&abs_old_path?, &abs_new_path?, Default::default()) .await }); @@ -1284,8 +1286,8 @@ impl LocalWorktree { let copy = cx.background_executor().spawn(async move { copy_recursive( fs.as_ref(), - &abs_old_path, - &abs_new_path, + &abs_old_path?, + &abs_new_path?, Default::default(), ) .await @@ -1609,11 +1611,17 @@ impl Snapshot { &self.abs_path } - pub fn absolutize(&self, path: &Path) -> PathBuf { + pub fn absolutize(&self, path: &Path) -> Result { + if path + .components() + .any(|component| !matches!(component, std::path::Component::Normal(_))) + { + return Err(anyhow!("invalid path")); + } if path.file_name().is_some() { - self.abs_path.join(path) + Ok(self.abs_path.join(path)) } else { - self.abs_path.to_path_buf() + Ok(self.abs_path.to_path_buf()) } } @@ -2823,7 +2831,7 @@ impl language::LocalFile for File { let abs_path = worktree.absolutize(&self.path); let fs = worktree.fs.clone(); cx.background_executor() - .spawn(async move { fs.load(&abs_path).await }) + .spawn(async move { fs.load(&abs_path?).await }) } fn buffer_reloaded( From ed76315387be762eafe032648c9bc6f9f9991a04 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 11:14:25 -0700 Subject: [PATCH 32/54] Fix prevention of cross-channel joins Co-Authored-By: Max --- crates/collab/src/lib.rs | 4 +- crates/collab/src/rpc.rs | 6 ++- crates/collab/src/tests/test_server.rs | 16 +++++- crates/workspace/src/workspace.rs | 70 +++++++++++++------------- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/crates/collab/src/lib.rs b/crates/collab/src/lib.rs index 85216525b0018c6d051c55a5882af8445f45c7d0..7dbf4513d936eebb6b627ace8ce868c9a0e9106b 100644 --- a/crates/collab/src/lib.rs +++ b/crates/collab/src/lib.rs @@ -88,7 +88,7 @@ impl std::fmt::Display for Error { impl std::error::Error for Error {} -#[derive(Default, Deserialize)] +#[derive(Deserialize)] pub struct Config { pub http_port: u16, pub database_url: String, @@ -100,7 +100,7 @@ pub struct Config { pub live_kit_secret: Option, pub rust_log: Option, pub log_json: Option, - pub zed_environment: String, + pub zed_environment: Arc, } #[derive(Default, Deserialize)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 835b48809da94dc60cd872d473e564a7456da81e..eb4f5e4ba9c2acf1ec7edbb0367509e89d62635e 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -104,6 +104,7 @@ impl Response { #[derive(Clone)] struct Session { + zed_environment: Arc, user_id: UserId, connection_id: ConnectionId, db: Arc>, @@ -609,6 +610,7 @@ impl Server { user_id, connection_id, db: Arc::new(tokio::sync::Mutex::new(DbHandle(this.app_state.db.clone()))), + zed_environment: this.app_state.config.zed_environment.clone(), peer: this.peer.clone(), connection_pool: this.connection_pool.clone(), live_kit_client: this.app_state.live_kit_client.clone(), @@ -999,7 +1001,7 @@ async fn join_room( room_id, session.user_id, session.connection_id, - RELEASE_CHANNEL_NAME.as_str(), + session.zed_environment.as_ref(), ) .await?; room_updated(&room.room, &session.peer); @@ -2608,7 +2610,7 @@ async fn join_channel_internal( channel_id, session.user_id, session.connection_id, - RELEASE_CHANNEL_NAME.as_str(), + session.zed_environment.as_ref(), ) .await?; diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index ae84729bac7c31a262dd97da990cc90a140f8669..034a85961f8e98966a2764c4196682fabf4b33cf 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -2,7 +2,7 @@ use crate::{ db::{tests::TestDb, NewUserParams, UserId}, executor::Executor, rpc::{Server, CLEANUP_TIMEOUT, RECONNECT_TIMEOUT}, - AppState, + AppState, Config, }; use anyhow::anyhow; use call::ActiveCall; @@ -414,7 +414,19 @@ impl TestServer { Arc::new(AppState { db: test_db.db().clone(), live_kit_client: Some(Arc::new(fake_server.create_api_client())), - config: Default::default(), + config: Config { + http_port: 0, + database_url: "".into(), + database_max_connections: 0, + api_token: "".into(), + invite_link_prefix: "".into(), + live_kit_server: None, + live_kit_key: None, + live_kit_secret: None, + rust_log: None, + log_json: None, + zed_environment: "test".into(), + }, }) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 826a6693d7ca350a85efa4589e5c87ba90bbf94c..ad02637ae39795c0e61ee38074062c518d12a3eb 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -26,12 +26,12 @@ use futures::{ }; use gpui::{ actions, canvas, div, impl_actions, point, size, Action, AnyElement, AnyModel, AnyView, - AnyWeakView, AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, - Bounds, Context, Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, - FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, - ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, - Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, - WindowBounds, WindowContext, WindowHandle, WindowOptions, + AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, Bounds, Context, + Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, + GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, ManagedView, Model, + ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, Render, Size, + Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, + WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -4034,34 +4034,34 @@ pub fn join_channel( return anyhow::Ok(()); } - if requesting_window.is_some() { - return anyhow::Ok(()); - } - // find an existing workspace to focus and show call controls - let mut active_window = activate_any_workspace_window(&mut cx); + let mut active_window = + requesting_window.or_else(|| activate_any_workspace_window(&mut cx)); if active_window.is_none() { // no open workspaces, make one to show the error in (blergh) - cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))? + let (window_handle, _) = cx + .update(|cx| { + Workspace::new_local(vec![], app_state.clone(), requesting_window, cx) + })? .await?; - } - active_window = activate_any_workspace_window(&mut cx); - let Some(active_window) = active_window else { - return anyhow::Ok(()); - }; + active_window = Some(window_handle); + } if let Err(err) = result { - active_window - .update(&mut cx, |_, cx| { - cx.prompt( - PromptLevel::Critical, - &format!("Failed to join channel: {}", err), - &["Ok"], - ) - })? - .await - .ok(); + log::error!("failed to join channel: {}", err); + if let Some(active_window) = active_window { + active_window + .update(&mut cx, |_, cx| { + cx.prompt( + PromptLevel::Critical, + &format!("Failed to join channel: {}", err), + &["Ok"], + ) + })? + .await + .ok(); + } } // return ok, we showed the error to the user. @@ -4079,19 +4079,17 @@ pub async fn get_any_active_workspace( cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), None, cx))? .await?; } - activate_any_workspace_window(&mut cx) - .context("could not open zed")? - .downcast::() - .context("could not open zed workspace window") + activate_any_workspace_window(&mut cx).context("could not open zed") } -fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option { +fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option> { cx.update(|cx| { for window in cx.windows() { - let is_workspace = window.downcast::().is_some(); - if is_workspace { - window.update(cx, |_, cx| cx.activate_window()).ok(); - return Some(window); + if let Some(workspace_window) = window.downcast::() { + workspace_window + .update(cx, |_, cx| cx.activate_window()) + .ok(); + return Some(workspace_window); } } None From 667d90180d03b746073128193b01a797dad66198 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 15:56:52 -0500 Subject: [PATCH 33/54] Improve chat panel empty states (#3955) This PR improves the empty states for the chat panel: - The signed-out state has been updated to match our other signed-out panel states. - A new state has been added to account for the case where a user is signed in but doesn't have an active chat. Release Notes: - Improved the design of empty states in the chat panel. --- crates/collab_ui/src/chat_panel.rs | 72 +++++++++++++++++++----------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index a13c0ed384f934d35a64cd29a5ebbc53070a7281..8bbfbe8c70cdfa9bf122dbd42d9583c00ccedd98 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -428,26 +428,42 @@ impl ChatPanel { rich_text::render_markdown(message.body.clone(), &mentions, language_registry, None) } - fn render_sign_in_prompt(&self, cx: &mut ViewContext) -> AnyElement { - Button::new("sign-in", "Sign in to use chat") - .on_click(cx.listener(move |this, _, cx| { - let client = this.client.clone(); - cx.spawn(|this, mut cx| async move { - if client - .authenticate_and_connect(true, &cx) - .log_err() - .await - .is_some() - { - this.update(&mut cx, |_, cx| { - cx.focus_self(); + fn render_sign_in_prompt(&self, cx: &mut ViewContext) -> impl IntoElement { + v_stack() + .gap_2() + .p_4() + .child( + Button::new("sign-in", "Sign in") + .style(ButtonStyle::Filled) + .icon_color(Color::Muted) + .icon(Icon::Github) + .icon_position(IconPosition::Start) + .full_width() + .on_click(cx.listener(move |this, _, cx| { + let client = this.client.clone(); + cx.spawn(|this, mut cx| async move { + if client + .authenticate_and_connect(true, &cx) + .log_err() + .await + .is_some() + { + this.update(&mut cx, |_, cx| { + cx.focus_self(); + }) + .ok(); + } }) - .ok(); - } - }) - .detach(); - })) - .into_any_element() + .detach(); + })), + ) + .child( + div().flex().w_full().items_center().child( + Label::new("Sign in to chat.") + .color(Color::Muted) + .size(LabelSize::Small), + ), + ) } fn send(&mut self, _: &Confirm, cx: &mut ViewContext) { @@ -550,12 +566,18 @@ impl EventEmitter for ChatPanel {} impl Render for ChatPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - div() - .full() - .child(if self.client.user_id().is_some() { - self.render_channel(cx) - } else { - self.render_sign_in_prompt(cx) + v_stack() + .size_full() + .map(|this| match (self.client.user_id(), self.active_chat()) { + (Some(_), Some(_)) => this.child(self.render_channel(cx)), + (Some(_), None) => this.child( + div().p_4().child( + Label::new("Select a channel to chat in.") + .size(LabelSize::Small) + .color(Color::Muted), + ), + ), + (None, _) => this.child(self.render_sign_in_prompt(cx)), }) .min_w(px(150.)) } From aed97f6c29324f8c1fbc3afd5322846070817140 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 14:07:18 -0700 Subject: [PATCH 34/54] Use "test" consistently --- crates/collab/src/db/tests/db_tests.rs | 4 ++-- crates/collab/src/rpc.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index 1f825efd74583d6754e762e54e1d5faa3b082889..5332f227ef4277ada2fce222bb7097ef0da396b3 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -455,7 +455,7 @@ async fn test_project_count(db: &Arc) { .unwrap(); let room_id = RoomId::from_proto( - db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "", "dev") + db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "", "test") .await .unwrap() .id, @@ -473,7 +473,7 @@ async fn test_project_count(db: &Arc) { room_id, user2.user_id, ConnectionId { owner_id, id: 1 }, - "dev", + "test", ) .await .unwrap(); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index eb4f5e4ba9c2acf1ec7edbb0367509e89d62635e..7300fdcd6c3d8b89c4e55634b354cf08dc77818a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -66,7 +66,6 @@ use time::OffsetDateTime; use tokio::sync::{watch, Semaphore}; use tower::ServiceBuilder; use tracing::{info_span, instrument, Instrument}; -use util::channel::RELEASE_CHANNEL_NAME; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); @@ -967,7 +966,7 @@ async fn create_room( session.user_id, session.connection_id, &live_kit_room, - RELEASE_CHANNEL_NAME.as_str(), + &session.zed_environment, ) .await?; From 80f204fabbe7f88c3d2757cc994d1f0d6f93436e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 16:45:18 -0500 Subject: [PATCH 35/54] Style the chat panel message input (#3956) This PR styles the message input in the chat panel. Screenshot 2024-01-08 at 4 28 33 PM Release Notes: - Improved the styling of the message editor in the chat panel. --- crates/collab_ui/src/chat_panel.rs | 19 ++---- .../src/chat_panel/message_editor.rs | 59 ++++++++++++++++--- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 8bbfbe8c70cdfa9bf122dbd42d9583c00ccedd98..4f8e12f1e8ea2be3ca2d5354a80e684e94269c22 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -19,7 +19,6 @@ use rich_text::RichText; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::sync::Arc; -use theme::ActiveTheme as _; use time::{OffsetDateTime, UtcOffset}; use ui::{prelude::*, Avatar, Button, Icon, IconButton, Label, TabBar, Tooltip}; use util::{ResultExt, TryFutureExt}; @@ -48,7 +47,7 @@ pub struct ChatPanel { languages: Arc, message_list: ListState, active_chat: Option<(Model, Subscription)>, - input_editor: View, + message_editor: View, local_timezone: UtcOffset, fs: Arc, width: Option, @@ -120,7 +119,7 @@ impl ChatPanel { message_list, active_chat: Default::default(), pending_serialization: Task::ready(None), - input_editor, + message_editor: input_editor, local_timezone: cx.local_timezone(), subscriptions: Vec::new(), workspace: workspace_handle, @@ -209,7 +208,7 @@ impl ChatPanel { self.message_list.reset(chat.message_count()); let channel_name = chat.channel(cx).map(|channel| channel.name.clone()); - self.input_editor.update(cx, |editor, cx| { + self.message_editor.update(cx, |editor, cx| { editor.set_channel(channel_id, channel_name, cx); }); }; @@ -300,13 +299,7 @@ impl ChatPanel { this } })) - .child( - div() - .z_index(1) - .p_2() - .bg(cx.theme().colors().background) - .child(self.input_editor.clone()), - ) + .child(h_stack().p_2().child(self.message_editor.clone())) .into_any() } @@ -469,7 +462,7 @@ impl ChatPanel { fn send(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some((chat, _)) = self.active_chat.as_ref() { let message = self - .input_editor + .message_editor .update(cx, |editor, cx| editor.take_message(cx)); if let Some(task) = chat @@ -585,7 +578,7 @@ impl Render for ChatPanel { impl FocusableView for ChatPanel { fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { - self.input_editor.read(cx).focus_handle(cx) + self.message_editor.read(cx).focus_handle(cx) } } diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 517fac4fbb377f425210d2468d3f18bb8d1ebb6a..7999db529a43985ae2b52cdde9f2108f9620b35c 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -1,16 +1,19 @@ +use std::{sync::Arc, time::Duration}; + use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams}; use client::UserId; use collections::HashMap; -use editor::{AnchorRangeExt, Editor}; +use editor::{AnchorRangeExt, Editor, EditorElement, EditorStyle}; use gpui::{ - AsyncWindowContext, FocusableView, IntoElement, Model, Render, SharedString, Task, View, - ViewContext, WeakView, + AsyncWindowContext, FocusableView, FontStyle, FontWeight, HighlightStyle, IntoElement, Model, + Render, SharedString, Task, TextStyle, View, ViewContext, WeakView, WhiteSpace, }; use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry}; use lazy_static::lazy_static; use project::search::SearchQuery; -use std::{sync::Arc, time::Duration}; -use workspace::item::ItemHandle; +use settings::Settings; +use theme::ThemeSettings; +use ui::prelude::*; const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50); @@ -181,7 +184,14 @@ impl MessageEditor { } editor.clear_highlights::(cx); - editor.highlight_text::(anchor_ranges, gpui::red().into(), cx) + editor.highlight_text::( + anchor_ranges, + HighlightStyle { + font_weight: Some(FontWeight::BOLD), + ..Default::default() + }, + cx, + ) }); this.mentions = mentioned_user_ids; @@ -196,8 +206,39 @@ impl MessageEditor { } impl Render for MessageEditor { - fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { - self.editor.to_any() + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let settings = ThemeSettings::get_global(cx); + let text_style = TextStyle { + color: if self.editor.read(cx).read_only(cx) { + cx.theme().colors().text_disabled + } else { + cx.theme().colors().text + }, + font_family: settings.ui_font.family.clone(), + font_features: settings.ui_font.features, + font_size: rems(0.875).into(), + font_weight: FontWeight::NORMAL, + font_style: FontStyle::Normal, + line_height: relative(1.3).into(), + background_color: None, + underline: None, + white_space: WhiteSpace::Normal, + }; + + div() + .w_full() + .px_2() + .py_1() + .bg(cx.theme().colors().editor_background) + .rounded_md() + .child(EditorElement::new( + &self.editor, + EditorStyle { + local_player: cx.theme().players().local(), + text: text_style, + ..Default::default() + }, + )) } } @@ -205,7 +246,7 @@ impl Render for MessageEditor { mod tests { use super::*; use client::{Client, User, UserStore}; - use gpui::{Context as _, TestAppContext, VisualContext as _}; + use gpui::TestAppContext; use language::{Language, LanguageConfig}; use rpc::proto; use settings::SettingsStore; From ae6d09b9b25abbdb0877eda35c8c79803bf47d1c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:49:14 +0100 Subject: [PATCH 36/54] chore: Extract `assets` module out of zed crate. (#3957) This essentially shaves off about 10% off of an incremental build after project change and potentially more if you're changing stuff like `welcome` that's very close to the `zed` crate in the dep graph. That's because macro expansion takes place even in incremental builds it seems? And zed (lib) + zed (bin) could take up to 4 seconds out of an incremental build, which is a *lot* in a 10s build. In reality though it shaves 1 second off of 5 seconds incremental 'welcome'/ 1s off of 10s 'project' builds. Note that we had `assets` crate in the past (removed in #2575 /cc @maxbrunsfeld), but this is a bit different, because `assets` is a dependency of *just* zed and nothing else. We essentially cache macro expansion results ourselves. Release Notes: - N/A --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + crates/assets/Cargo.toml | 11 +++++++++++ crates/{zed/src/assets.rs => assets/src/lib.rs} | 1 + crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 5 +++-- crates/zed/src/zed.rs | 3 +-- 7 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 crates/assets/Cargo.toml rename crates/{zed/src/assets.rs => assets/src/lib.rs} (82%) diff --git a/Cargo.lock b/Cargo.lock index b9cb8ad4d1170ae8c33677d41fe5e94a6144567f..ec2e544dd54a68dd2235ac2887a56d1a7e02e3fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,15 @@ 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 = "assistant" version = "0.1.0" @@ -9529,6 +9538,7 @@ dependencies = [ "activity_indicator", "ai", "anyhow", + "assets", "assistant", "async-compression", "async-recursion 0.3.2", diff --git a/Cargo.toml b/Cargo.toml index 008b8406ecbb9c655b1d64406f697053cb5714c2..79d28821d4b040f35fc1ab1dd391cddeee93bc47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "crates/assets", "crates/activity_indicator", "crates/ai", "crates/assistant", diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..18e6c7fa659c3bced7721b865e51fc5625f50ee3 --- /dev/null +++ b/crates/assets/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "assets" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gpui = {path = "../gpui"} +rust-embed.workspace = true +anyhow.workspace = true diff --git a/crates/zed/src/assets.rs b/crates/assets/src/lib.rs similarity index 82% rename from crates/zed/src/assets.rs rename to crates/assets/src/lib.rs index 5d5e81a60e4feaff4c04bf3a0f3aff9a87659686..010b7ebda3d1be4532f47d6b5e7cdb79088694e2 100644 --- a/crates/zed/src/assets.rs +++ b/crates/assets/src/lib.rs @@ -1,3 +1,4 @@ +// This crate was essentially pulled out verbatim from main `zed` crate to avoid having to run RustEmbed macro whenever zed has to be rebuilt. It saves a second or two on an incremental build. use anyhow::anyhow; use gpui::{AssetSource, Result, SharedString}; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index ae2c3701d6ece648f58204ac38dabc5f668a0def..734c225cb1e610c64dab92112accad9634632fee 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -74,6 +74,7 @@ vim = { path = "../vim" } workspace = { path = "../workspace" } welcome = { path = "../welcome" } zed_actions = {path = "../zed_actions"} +assets = {path = "../assets"} anyhow.workspace = true async-compression.workspace = true async-tar = "0.4.2" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 56109d9c9a532d97de0f8b76101b4057203879e2..e10c52a175c8633fa3b3bebdb09223f3505587ad 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -16,6 +16,7 @@ use isahc::{prelude::Configurable, Request}; use language::LanguageRegistry; use log::LevelFilter; +use assets::Assets; use node_runtime::RealNodeRuntime; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; @@ -49,8 +50,8 @@ use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN}; use workspace::{AppState, WorkspaceStore}; use zed::{ app_menus, build_window_options, ensure_only_instance, handle_cli_connection, - handle_keymap_file_changes, initialize_workspace, languages, Assets, IsOnlyInstance, - OpenListener, OpenRequest, + handle_keymap_file_changes, initialize_workspace, languages, IsOnlyInstance, OpenListener, + OpenRequest, }; fn main() { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b77ba1a23945000bd7f8d6b9edf6c4c486fef008..73368a988377e8edb927fb1d629c78385112a492 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,11 +1,9 @@ mod app_menus; -mod assets; pub mod languages; mod only_instance; mod open_listener; pub use app_menus::*; -pub use assets::*; use assistant::AssistantPanel; use breadcrumbs::Breadcrumbs; use collections::VecDeque; @@ -18,6 +16,7 @@ pub use only_instance::*; pub use open_listener::*; use anyhow::{anyhow, Context as _}; +use assets::Assets; use futures::{channel::mpsc, select_biased, StreamExt}; use project_panel::ProjectPanel; use quick_action_bar::QuickActionBar; From 71149bc7ccdebaa38d5cae8e4e66a21d8ab88075 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 13:56:52 -0700 Subject: [PATCH 37/54] Fix relative path opening from project symbols Co-Authored-By: Max --- crates/collab/src/tests/integration_tests.rs | 6 +++--- crates/file_finder/src/file_finder.rs | 2 +- crates/project/src/project.rs | 17 ++++++++++++++++- crates/project/src/project_tests.rs | 10 ++-------- crates/semantic_index/src/semantic_index.rs | 12 +++++------- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 457f085f8fe9a1d6de8df497fbff435277f6cfef..a21235b6f3914a8cc6e181c9ae128da011f8bce3 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -4936,10 +4936,10 @@ async fn test_project_symbols( .await .unwrap(); - buffer_b_2.read_with(cx_b, |buffer, _| { + buffer_b_2.read_with(cx_b, |buffer, cx| { assert_eq!( - buffer.file().unwrap().path().as_ref(), - Path::new("../crate-2/two.rs") + buffer.file().unwrap().full_path(cx), + Path::new("/code/crate-2/two.rs") ); }); diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index ce68819646c9911ff8d89037527e1743d8c59fb7..d49eb9ee603d7e819bab5097adac874107fd9a3f 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1297,7 +1297,7 @@ mod tests { // so that one should be sorted earlier let b_path = ProjectPath { worktree_id, - path: Arc::from(Path::new("/root/dir2/b.txt")), + path: Arc::from(Path::new("dir2/b.txt")), }; workspace .update(cx, |workspace, cx| { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 584638a47a355495d2ffdf9f7da4712c23e30c4a..044b750ad9d7cecce1a00f491e0fd66199989bb7 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4732,7 +4732,8 @@ impl Project { } else { return Task::ready(Err(anyhow!("worktree not found for symbol"))); }; - let symbol_abs_path = worktree_abs_path.join(&symbol.path.path); + + let symbol_abs_path = resolve_path(worktree_abs_path, &symbol.path.path); let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) { uri } else { @@ -8725,6 +8726,20 @@ fn relativize_path(base: &Path, path: &Path) -> PathBuf { components.iter().map(|c| c.as_os_str()).collect() } +fn resolve_path(base: &Path, path: &Path) -> PathBuf { + let mut result = base.to_path_buf(); + for component in path.components() { + match component { + Component::ParentDir => { + result.pop(); + } + Component::CurDir => (), + _ => result.push(component), + } + } + result +} + impl Item for Buffer { fn entry_id(&self, cx: &AppContext) -> Option { File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx)) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 5c3c21fd08c988a0987a1e1acf0a3c8eb28e38d7..e90d3237127efa3b29d75ee110a7bbd27caa6de9 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -4337,20 +4337,14 @@ async fn test_create_entry(cx: &mut gpui::TestAppContext) { ] ); - // ************************************ - // Note: unsure if this is the best fix for the integration failure, but assuming we want - // to keep that behavior, then this test should cover it - // ************************************ - - // But we can open buffers with '..' + // And we cannot open buffers with '..' let result = project .update(cx, |project, cx| { let id = project.worktrees().next().unwrap().read(cx).id(); project.open_buffer((id, "../c.rs"), cx) }) .await; - - assert!(dbg!(result).is_ok()) + assert!(result.is_err()) } async fn search( diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index dbcdeee5ed0751d96a086fea691cd1195c2b81cc..81c4fbbc3d04b0d0bca6e4b70c074e3f75467999 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -559,7 +559,7 @@ impl SemanticIndex { .spawn(async move { let mut changed_paths = BTreeMap::new(); for file in worktree.files(false, 0) { - let absolute_path = worktree.absolutize(&file.path); + let absolute_path = worktree.absolutize(&file.path)?; if file.is_external || file.is_ignored || file.is_symlink { continue; @@ -1068,11 +1068,10 @@ impl SemanticIndex { return true; }; - worktree_state.changed_paths.retain(|path, info| { + for (path, info) in &worktree_state.changed_paths { if info.is_deleted { files_to_delete.push((worktree_state.db_id, path.clone())); - } else { - let absolute_path = worktree.read(cx).absolutize(path); + } else if let Ok(absolute_path) = worktree.read(cx).absolutize(path) { let job_handle = JobHandle::new(pending_file_count_tx); pending_files.push(PendingFile { absolute_path, @@ -1083,9 +1082,8 @@ impl SemanticIndex { worktree_db_id: worktree_state.db_id, }); } - - false - }); + } + worktree_state.changed_paths.clear(); true }); From d7c5d29237b1a010f09559b291fc45c3bde450d2 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 15:39:24 -0700 Subject: [PATCH 38/54] Only allow read-write users to update buffers --- crates/collab/src/db/ids.rs | 8 ++++ crates/collab/src/db/queries/projects.rs | 56 ++++++++++++++++++++++-- crates/collab/src/rpc.rs | 26 ++++------- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/crates/collab/src/db/ids.rs b/crates/collab/src/db/ids.rs index 2e2218c4d9da181719cf6404245a4d84d4285dab..9f77225fb704c1d4d53051cb7ee29d77c3536f80 100644 --- a/crates/collab/src/db/ids.rs +++ b/crates/collab/src/db/ids.rs @@ -148,6 +148,14 @@ impl ChannelRole { Guest | Banned => false, } } + + pub fn can_read_projects(&self) -> bool { + use ChannelRole::*; + match self { + Admin | Member | Guest => true, + Banned => false, + } + } } impl From for ChannelRole { diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 04c77c80771b29a6be3d1603898b681da3e79569..6e1bf16309bd69315903a37ce7b57d389e749161 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -805,6 +805,43 @@ impl Database { .map(|guard| guard.into_inner()) } + pub async fn host_for_read_only_project_request( + &self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + let current_participant = room_participant::Entity::find() + .filter(room_participant::Column::RoomId.eq(room_id)) + .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.id)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such room"))?; + + if !current_participant + .role + .map_or(false, |role| role.can_read_projects()) + { + Err(anyhow!("not authorized to read projects"))?; + } + + let host = project_collaborator::Entity::find() + .filter( + project_collaborator::Column::ProjectId + .eq(project_id) + .and(project_collaborator::Column::IsHost.eq(true)), + ) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("failed to read project host"))?; + + Ok(host.connection()) + }) + .await + .map(|guard| guard.into_inner()) + } + pub async fn host_for_mutating_project_request( &self, project_id: ProjectId, @@ -821,8 +858,7 @@ impl Database { if !current_participant .role - .unwrap_or(ChannelRole::Guest) - .can_edit_projects() + .map_or(false, |role| role.can_edit_projects()) { Err(anyhow!("not authorized to edit projects"))?; } @@ -843,13 +879,27 @@ impl Database { .map(|guard| guard.into_inner()) } - pub async fn project_collaborators( + pub async fn project_collaborators_for_buffer_update( &self, project_id: ProjectId, connection_id: ConnectionId, ) -> Result>> { let room_id = self.room_id_for_project(project_id).await?; self.room_transaction(room_id, |tx| async move { + let current_participant = room_participant::Entity::find() + .filter(room_participant::Column::RoomId.eq(room_id)) + .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.id)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such room"))?; + + if !current_participant + .role + .map_or(false, |role| role.can_edit_projects()) + { + Err(anyhow!("not authorized to edit projects"))?; + } + let collaborators = project_collaborator::Entity::find() .filter(project_collaborator::Column::ProjectId.eq(project_id)) .all(&*tx) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 572670d78f4dca73cff7e57b8d0f0b030d9bdd11..da0d281f102602a3f7ec38a0d97b9e801a9e35ff 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -227,7 +227,7 @@ impl Server { .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) - .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_mutating_project_request::) .add_request_handler( forward_mutating_project_request::, @@ -1750,24 +1750,15 @@ where T: EntityMessage + RequestMessage, { let project_id = ProjectId::from_proto(request.remote_entity_id()); - let host_connection_id = { - let collaborators = session - .db() - .await - .project_collaborators(project_id, session.connection_id) - .await?; - collaborators - .iter() - .find(|collaborator| collaborator.is_host) - .ok_or_else(|| anyhow!("host not found"))? - .connection_id - }; - + let host_connection_id = session + .db() + .await + .host_for_read_only_project_request(project_id, session.connection_id) + .await?; let payload = session .peer .forward_request(session.connection_id, host_connection_id, request) .await?; - response.send(payload)?; Ok(()) } @@ -1786,12 +1777,10 @@ where .await .host_for_mutating_project_request(project_id, session.connection_id) .await?; - let payload = session .peer .forward_request(session.connection_id, host_connection_id, request) .await?; - response.send(payload)?; Ok(()) } @@ -1823,11 +1812,12 @@ async fn update_buffer( let project_id = ProjectId::from_proto(request.project_id); let mut guest_connection_ids; let mut host_connection_id = None; + { let collaborators = session .db() .await - .project_collaborators(project_id, session.connection_id) + .project_collaborators_for_buffer_update(project_id, session.connection_id) .await?; guest_connection_ids = Vec::with_capacity(collaborators.len() - 1); for collaborator in collaborators.iter() { From fac8ebf2ccdf863c0d494fc3c18e180e4f3ecb30 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 17:46:25 -0500 Subject: [PATCH 39/54] Fix error border color for email input in feedback dialog (#3959) This PR fixes the border color used when the email input in the feedback dialog is invalid. Previously this was hardcoded just to `red` instead of using the appropriate color from the theme. Screenshot 2024-01-08 at 5 40 07 PM Release Notes: - Fixed the border color used for the email input in the feedback dialog when an invalid email is entered. --- crates/feedback/src/feedback_modal.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index 6c5308c1c64e3b6339dbdfff714c5a317dbe7842..566d34dadd20baf740eb8e3a2d2be1d87c655684 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -7,7 +7,7 @@ use db::kvp::KEY_VALUE_STORE; use editor::{Editor, EditorEvent}; use futures::AsyncReadExt; use gpui::{ - div, red, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model, + div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model, PromptLevel, Render, Task, View, ViewContext, }; use isahc::Request; @@ -476,7 +476,7 @@ impl Render for FeedbackModal { .border_color(if self.valid_email_address() { cx.theme().colors().border } else { - red() + cx.theme().status().error_border }) .child(self.email_address_editor.clone()), ) From e876262579c3a52273b26fdce4bb182b4ae9fc64 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 15:47:07 -0700 Subject: [PATCH 40/54] collab 0.35.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 ec2e544dd54a68dd2235ac2887a56d1a7e02e3fd..00c13d10ff899169f469d0fbe233b9e75a3649f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.34.0" +version = "0.35.0" dependencies = [ "anyhow", "async-trait", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 498ded6d9a70f144bda9aa94259b62c129ecc568..baf279d6340d5024f18cdd1beb863aa76c549326 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.34.0" +version = "0.35.0" publish = false [[bin]] From 32cd95674fb741b0f2e3ebcd07333a072ce89a77 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 15:49:03 -0700 Subject: [PATCH 41/54] Push branch before tag --- script/lib/bump-version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/lib/bump-version.sh b/script/lib/bump-version.sh index 0e1dfa5131d6a72b4d305511c9e80fab209aaba1..8be7e0b6c80c90262245f0b95e8efd87f38eb43d 100755 --- a/script/lib/bump-version.sh +++ b/script/lib/bump-version.sh @@ -30,7 +30,7 @@ Locally committed and tagged ${package} version ${new_version} To push this: - git push origin ${tag_name} ${branch_name} + git push origin ${branch_name} ${tag_name} To undo this: From 4007b2f72bc67b71d66fccc67a10e6462f9cf063 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 18:28:37 -0500 Subject: [PATCH 42/54] Tighten up feedback modal design (#3960) This PR tightens up the design of the feedback dialog: Screenshot 2024-01-08 at 6 20 50 PM Screenshot 2024-01-08 at 6 21 24 PM Release Notes: - Improved the design of the feedback dialog. --- crates/feedback/src/feedback_modal.rs | 60 +++++++++++++-------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index 566d34dadd20baf740eb8e3a2d2be1d87c655684..b197d602338e052b319623e9b1ad6a1e6f7d7b53 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -179,14 +179,13 @@ impl FeedbackModal { editor }); - // Moved here because providing it inline breaks rustfmt - let placeholder_text = - "You can use markdown to organize your feedback with code and links."; - let feedback_editor = cx.new_view(|cx| { let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx); - editor.set_placeholder_text(placeholder_text, cx); - // editor.set_show_gutter(false, cx); + editor.set_placeholder_text( + "You can use markdown to organize your feedback with code and links.", + cx, + ); + editor.set_show_gutter(false, cx); editor.set_vertical_scroll_margin(5, cx); editor }); @@ -422,10 +421,6 @@ impl Render for FeedbackModal { let open_community_repo = cx.listener(|_, _, cx| cx.dispatch_action(Box::new(OpenZedCommunityRepo))); - // Moved this here because providing it inline breaks rustfmt - let provide_an_email_address = - "Provide an email address if you want us to be able to reply."; - v_stack() .elevation_3(cx) .key_context("GiveFeedback") @@ -434,11 +429,8 @@ impl Render for FeedbackModal { .max_w(rems(96.)) .h(rems(32.)) .p_4() - .gap_4() - .child(v_stack().child( - // TODO: Add Headline component to `ui2` - div().text_xl().child("Share Feedback"), - )) + .gap_2() + .child(Headline::new("Share Feedback")) .child( Label::new(if self.character_count < *FEEDBACK_CHAR_LIMIT.start() { format!( @@ -468,17 +460,26 @@ impl Render for FeedbackModal { .child(self.feedback_editor.clone()), ) .child( - h_stack() - .bg(cx.theme().colors().editor_background) - .p_2() - .border() - .rounded_md() - .border_color(if self.valid_email_address() { - cx.theme().colors().border - } else { - cx.theme().status().error_border - }) - .child(self.email_address_editor.clone()), + v_stack() + .gap_1() + .child( + h_stack() + .bg(cx.theme().colors().editor_background) + .p_2() + .border() + .rounded_md() + .border_color(if self.valid_email_address() { + cx.theme().colors().border + } else { + cx.theme().status().error_border + }) + .child(self.email_address_editor.clone()), + ) + .child( + Label::new("Provide an email address if you want us to be able to reply.") + .size(LabelSize::Small) + .color(Color::Muted), + ), ) .child( h_stack() @@ -515,12 +516,7 @@ impl Render for FeedbackModal { this.submit(cx).detach(); })) .tooltip(move |cx| { - Tooltip::with_meta( - "Submit feedback to the Zed team.", - None, - provide_an_email_address, - cx, - ) + Tooltip::text("Submit feedback to the Zed team.", cx) }) .when(!self.can_submit(), |this| this.disabled(true)), ), From 51caa743495ec237d528d9e4330c1b2b9b0115e4 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 8 Jan 2024 15:46:04 -0800 Subject: [PATCH 43/54] Restore the active pane magnification feature --- crates/workspace/src/pane_group.rs | 65 +++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 4428e42830be725fb79979d6acf3e65a777d9386..68fd3c084f591e8bd7401c66e056e38beada1278 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -579,12 +579,15 @@ mod element { Size, Style, WeakView, WindowContext, }; use parking_lot::Mutex; + use settings::Settings; use smallvec::SmallVec; use ui::prelude::*; use util::ResultExt; use crate::Workspace; + use crate::WorkspaceSettings; + use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE}; const DIVIDER_SIZE: f32 = 1.0; @@ -834,20 +837,39 @@ mod element { debug_assert!(flexes.len() == len); debug_assert!(flex_values_in_bounds(flexes.as_slice())); + let magnification_value = WorkspaceSettings::get(None, cx).active_pane_magnification; + let active_pane_magnification = if magnification_value == 1. { + None + } else { + Some(magnification_value) + }; + + let total_flex = if let Some(flex) = active_pane_magnification { + self.children.len() as f32 - 1. + flex + } else { + len as f32 + }; + let mut origin = bounds.origin; - let space_per_flex = bounds.size.along(self.axis) / len as f32; + let space_per_flex = bounds.size.along(self.axis) / total_flex; let mut bounding_boxes = self.bounding_boxes.lock(); bounding_boxes.clear(); for (ix, child) in self.children.iter_mut().enumerate() { - //todo!(active_pane_magnification) - // If using active pane magnification, need to switch to using - // 1 for all non-active panes, and then the magnification for the - // active pane. + let child_flex = active_pane_magnification + .map(|magnification| { + if self.active_pane_ix == Some(ix) { + magnification + } else { + 1. + } + }) + .unwrap_or_else(|| flexes[ix]); + let child_size = bounds .size - .apply_along(self.axis, |_| space_per_flex * flexes[ix]); + .apply_along(self.axis, |_| space_per_flex * child_flex); let child_bounds = Bounds { origin, @@ -857,20 +879,23 @@ mod element { cx.with_z_index(0, |cx| { child.draw(origin, child_size.into(), cx); }); - cx.with_z_index(1, |cx| { - if ix < len - 1 { - Self::push_handle( - self.flexes.clone(), - state.clone(), - self.axis, - ix, - child_bounds, - bounds, - self.workspace.clone(), - cx, - ); - } - }); + + if active_pane_magnification.is_none() { + cx.with_z_index(1, |cx| { + if ix < len - 1 { + Self::push_handle( + self.flexes.clone(), + state.clone(), + self.axis, + ix, + child_bounds, + bounds, + self.workspace.clone(), + cx, + ); + } + }); + } origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis)); } From 6f6fa5b79eb04141cda85bb432c754cc8e567876 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 8 Jan 2024 16:53:01 -0700 Subject: [PATCH 44/54] Fix assets build --- crates/assets/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml index 18e6c7fa659c3bced7721b865e51fc5625f50ee3..7ebae21d7dddb868d85dd9f11ae7fd8a34a277f7 100644 --- a/crates/assets/Cargo.toml +++ b/crates/assets/Cargo.toml @@ -2,6 +2,7 @@ name = "assets" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From ec7db3f52898119b807b808479d0f5b425db0be4 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 8 Jan 2024 16:02:39 -0800 Subject: [PATCH 45/54] Restore the terminal cursor settings --- crates/terminal_view/src/terminal_element.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index d936716032a53b432d2f6f1a5dc6b79069656c8b..bcaf147af239f9e36111a0a50980561ac4befe33 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -451,6 +451,18 @@ impl TerminalElement { } }); + let interactive_text_bounds = InteractiveBounds { + bounds, + stacking_order: cx.stacking_order().clone(), + }; + if interactive_text_bounds.visibly_contains(&cx.mouse_position(), cx) { + if self.can_navigate_to_selected_word && last_hovered_word.is_some() { + cx.set_cursor_style(gpui::CursorStyle::PointingHand) + } else { + cx.set_cursor_style(gpui::CursorStyle::IBeam) + } + } + let hyperlink_tooltip = last_hovered_word.clone().map(|hovered_word| { div() .size_full() From 0684369734e7c7fddb12ab3af0c12d400aadc795 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 8 Jan 2024 17:30:24 -0800 Subject: [PATCH 46/54] Fix off by 1 error when computing available key bindings --- crates/gpui/src/key_dispatch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 22c4dffc03a78df8fde5530a3059887e91a2b876..81f66746c536e73a716b5c5559f4385fcbe5d70b 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -192,8 +192,8 @@ impl DispatchTree { keymap .bindings_for_action(action) .filter(|binding| { - for i in 1..context_stack.len() { - let context = &context_stack[0..i]; + for i in 0..context_stack.len() { + let context = &context_stack[0..=i]; if keymap.binding_enabled(binding, context) { return true; } From c40a7f344558580e48156f2c6a107a7491fc7b15 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 20:51:14 -0500 Subject: [PATCH 47/54] Stop propagation when deploying the context menu for a project panel entry (#3965) This PR fixes an issue where right-click on any project panel entry would cause the context menu on the root of the project panel (introduced in #3954) to deploy. We need to stop propagation in the handler on the inner project panel list items so that the click event doesn't bubble up the tree. Release Notes: - Fixed an issue where the project panel was always deploying the root context menu rather than on the clicked item. --- crates/project_panel/src/project_panel.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index ee016f399e0b2979423f5d5dc625d616013526d3..727ab7e859d237c8729401e6dcee90f8529616c8 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1433,6 +1433,9 @@ impl ProjectPanel { })) .on_secondary_mouse_down(cx.listener( move |this, event: &MouseDownEvent, cx| { + // Stop propagation to prevent the catch-all context menu for the project + // panel from being deployed. + cx.stop_propagation(); this.deploy_context_menu(event.position, entry_id, cx); }, )), From 4afa5fb23e6c791bde56c23fa6c57a19fb233395 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 21:54:59 -0500 Subject: [PATCH 48/54] Add stories for collab notifications (#3967) This PR adds some basic stories for collab notifications to make them easier to work on: Screenshot 2024-01-08 at 9 43 39 PM I factored out a `CollabNotification` component that defines the general structure for one of these notifications, and this is the component that we use in the stories, with representative values passed to it to simulate the different instances of the notification. We can't use the actual notification components in the stories due to their data dependencies. Release Notes: - N/A --- Cargo.lock | 2 + crates/collab_ui/Cargo.toml | 3 + crates/collab_ui/src/notifications.rs | 11 ++- .../src/notifications/collab_notification.rs | 52 +++++++++++++ .../incoming_call_notification.rs | 52 +++++-------- .../project_shared_notification.rs | 74 +++++++------------ crates/collab_ui/src/notifications/stories.rs | 3 + .../stories/collab_notification.rs | 50 +++++++++++++ crates/storybook/Cargo.toml | 1 + crates/storybook/src/story_selector.rs | 4 + 10 files changed, 169 insertions(+), 83 deletions(-) create mode 100644 crates/collab_ui/src/notifications/collab_notification.rs create mode 100644 crates/collab_ui/src/notifications/stories.rs create mode 100644 crates/collab_ui/src/notifications/stories/collab_notification.rs diff --git a/Cargo.lock b/Cargo.lock index 00c13d10ff899169f469d0fbe233b9e75a3649f1..c6e7ecebc1300628462750ed714b9103e6a001c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1559,6 +1559,7 @@ dependencies = [ "serde_json", "settings", "smallvec", + "story", "theme", "theme_selector", "time", @@ -7447,6 +7448,7 @@ dependencies = [ "backtrace-on-stack-overflow", "chrono", "clap 4.4.4", + "collab_ui", "dialoguer", "editor", "fuzzy", diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index f845de3a939886fefa42343131e0c4ec18543fea..84c1810bc841d904a7a534fb562671dc9d7232c9 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -9,6 +9,8 @@ path = "src/collab_ui.rs" doctest = false [features] +default = [] +stories = ["dep:story"] test-support = [ "call/test-support", "client/test-support", @@ -44,6 +46,7 @@ project = { path = "../project" } recent_projects = { path = "../recent_projects" } rpc = { path = "../rpc" } settings = { path = "../settings" } +story = { path = "../story", optional = true } feature_flags = { path = "../feature_flags"} theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs index 5c184ec5c86ab268f4455b21d855ca118d40d50b..7759fef52059fb47dbc74a3803be309dded114c1 100644 --- a/crates/collab_ui/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -1,9 +1,16 @@ +mod collab_notification; +pub mod incoming_call_notification; +pub mod project_shared_notification; + +#[cfg(feature = "stories")] +mod stories; + use gpui::AppContext; use std::sync::Arc; use workspace::AppState; -pub mod incoming_call_notification; -pub mod project_shared_notification; +#[cfg(feature = "stories")] +pub use stories::*; pub fn init(app_state: &Arc, cx: &mut AppContext) { incoming_call_notification::init(app_state, cx); diff --git a/crates/collab_ui/src/notifications/collab_notification.rs b/crates/collab_ui/src/notifications/collab_notification.rs new file mode 100644 index 0000000000000000000000000000000000000000..e2ef06f9a58f8b131f166ab2a83250839f076ca2 --- /dev/null +++ b/crates/collab_ui/src/notifications/collab_notification.rs @@ -0,0 +1,52 @@ +use gpui::{img, prelude::*, AnyElement}; +use smallvec::SmallVec; +use ui::prelude::*; + +#[derive(IntoElement)] +pub struct CollabNotification { + avatar_uri: SharedString, + accept_button: Button, + dismiss_button: Button, + children: SmallVec<[AnyElement; 2]>, +} + +impl CollabNotification { + pub fn new( + avatar_uri: impl Into, + accept_button: Button, + dismiss_button: Button, + ) -> Self { + Self { + avatar_uri: avatar_uri.into(), + accept_button, + dismiss_button, + children: SmallVec::new(), + } + } +} + +impl ParentElement for CollabNotification { + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + &mut self.children + } +} + +impl RenderOnce for CollabNotification { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + h_stack() + .text_ui() + .justify_between() + .size_full() + .overflow_hidden() + .elevation_3(cx) + .p_2() + .gap_2() + .child(img(self.avatar_uri).w_12().h_12().rounded_full()) + .child(v_stack().overflow_hidden().children(self.children)) + .child( + v_stack() + .child(self.accept_button) + .child(self.dismiss_button), + ) + } +} diff --git a/crates/collab_ui/src/notifications/incoming_call_notification.rs b/crates/collab_ui/src/notifications/incoming_call_notification.rs index fa28ef9a6030d4009e96d170e7f7aefc46e52783..223415119fca00cca478d56423b20f9b44417c2e 100644 --- a/crates/collab_ui/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui/src/notifications/incoming_call_notification.rs @@ -1,15 +1,12 @@ use crate::notification_window_options; +use crate::notifications::collab_notification::CollabNotification; use call::{ActiveCall, IncomingCall}; use futures::StreamExt; -use gpui::{ - img, px, AppContext, ParentElement, Render, RenderOnce, Styled, ViewContext, - VisualContext as _, WindowHandle, -}; +use gpui::{prelude::*, AppContext, WindowHandle}; use settings::Settings; use std::sync::{Arc, Weak}; use theme::ThemeSettings; -use ui::prelude::*; -use ui::{h_stack, v_stack, Button, Label}; +use ui::{prelude::*, Button, Label}; use util::ResultExt; use workspace::AppState; @@ -31,8 +28,8 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { if let Some(incoming_call) = incoming_call { let unique_screens = cx.update(|cx| cx.displays()).unwrap(); let window_size = gpui::Size { - width: px(380.), - height: px(64.), + width: px(400.), + height: px(72.), }; for screen in unique_screens { @@ -129,35 +126,22 @@ impl Render for IncomingCallNotification { cx.set_rem_size(ui_font_size); - h_stack() - .font(ui_font) - .text_ui() - .justify_between() - .size_full() - .overflow_hidden() - .elevation_3(cx) - .p_2() - .gap_2() - .child( - img(self.state.call.calling_user.avatar_uri.clone()) - .w_12() - .h_12() - .rounded_full(), + div().size_full().font(ui_font).child( + CollabNotification::new( + self.state.call.calling_user.avatar_uri.clone(), + Button::new("accept", "Accept").on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(true, cx) + }), + Button::new("decline", "Decline").on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(false, cx) + }), ) .child(v_stack().overflow_hidden().child(Label::new(format!( "{} is sharing a project in Zed", self.state.call.calling_user.github_login - )))) - .child( - v_stack() - .child(Button::new("accept", "Accept").render(cx).on_click({ - let state = self.state.clone(); - move |_, cx| state.respond(true, cx) - })) - .child(Button::new("decline", "Decline").render(cx).on_click({ - let state = self.state.clone(); - move |_, cx| state.respond(false, cx) - })), - ) + )))), + ) } } diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index 982214c3e596e7290a4837264cc570c5aa786c94..79adc69a801dbe5ed30c7b9afb8d1df19bba6c5e 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -1,12 +1,13 @@ use crate::notification_window_options; +use crate::notifications::collab_notification::CollabNotification; use call::{room, ActiveCall}; use client::User; use collections::HashMap; -use gpui::{img, px, AppContext, ParentElement, Render, Size, Styled, ViewContext, VisualContext}; +use gpui::{AppContext, Size}; use settings::Settings; use std::sync::{Arc, Weak}; use theme::ThemeSettings; -use ui::{h_stack, prelude::*, v_stack, Button, Label}; +use ui::{prelude::*, Button, Label}; use workspace::AppState; pub fn init(app_state: &Arc, cx: &mut AppContext) { @@ -130,51 +131,30 @@ impl Render for ProjectSharedNotification { cx.set_rem_size(ui_font_size); - h_stack() - .font(ui_font) - .text_ui() - .justify_between() - .size_full() - .overflow_hidden() - .elevation_3(cx) - .p_2() - .gap_2() - .child( - img(self.owner.avatar_uri.clone()) - .w_12() - .h_12() - .rounded_full(), - ) - .child( - v_stack() - .overflow_hidden() - .child(Label::new(self.owner.github_login.clone())) - .child(Label::new(format!( - "is sharing a project in Zed{}", - if self.worktree_root_names.is_empty() { - "" - } else { - ":" - } - ))) - .children(if self.worktree_root_names.is_empty() { - None - } else { - Some(Label::new(self.worktree_root_names.join(", "))) - }), - ) - .child( - v_stack() - .child(Button::new("open", "Open").on_click(cx.listener( - move |this, _event, cx| { - this.join(cx); - }, - ))) - .child(Button::new("dismiss", "Dismiss").on_click(cx.listener( - move |this, _event, cx| { - this.dismiss(cx); - }, - ))), + div().size_full().font(ui_font).child( + CollabNotification::new( + self.owner.avatar_uri.clone(), + Button::new("open", "Open").on_click(cx.listener(move |this, _event, cx| { + this.join(cx); + })), + Button::new("dismiss", "Dismiss").on_click(cx.listener(move |this, _event, cx| { + this.dismiss(cx); + })), ) + .child(Label::new(self.owner.github_login.clone())) + .child(Label::new(format!( + "is sharing a project in Zed{}", + if self.worktree_root_names.is_empty() { + "" + } else { + ":" + } + ))) + .children(if self.worktree_root_names.is_empty() { + None + } else { + Some(Label::new(self.worktree_root_names.join(", "))) + }), + ) } } diff --git a/crates/collab_ui/src/notifications/stories.rs b/crates/collab_ui/src/notifications/stories.rs new file mode 100644 index 0000000000000000000000000000000000000000..36518679c661627346e45357161ba117a6bfe5b3 --- /dev/null +++ b/crates/collab_ui/src/notifications/stories.rs @@ -0,0 +1,3 @@ +mod collab_notification; + +pub use collab_notification::*; diff --git a/crates/collab_ui/src/notifications/stories/collab_notification.rs b/crates/collab_ui/src/notifications/stories/collab_notification.rs new file mode 100644 index 0000000000000000000000000000000000000000..c43cac46d21352ac8375e33cf530891385c36da0 --- /dev/null +++ b/crates/collab_ui/src/notifications/stories/collab_notification.rs @@ -0,0 +1,50 @@ +use gpui::prelude::*; +use story::{StoryContainer, StoryItem, StorySection}; +use ui::prelude::*; + +use crate::notifications::collab_notification::CollabNotification; + +pub struct CollabNotificationStory; + +impl Render for CollabNotificationStory { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + let window_container = |width, height| div().w(px(width)).h(px(height)); + + StoryContainer::new( + "CollabNotification Story", + "crates/collab_ui/src/notifications/stories/collab_notification.rs", + ) + .child( + StorySection::new().child(StoryItem::new( + "Incoming Call Notification", + window_container(400., 72.).child( + CollabNotification::new( + "https://avatars.githubusercontent.com/u/1486634?v=4", + Button::new("accept", "Accept"), + Button::new("decline", "Decline"), + ) + .child( + v_stack() + .overflow_hidden() + .child(Label::new("maxdeviant is sharing a project in Zed")), + ), + ), + )), + ) + .child( + StorySection::new().child(StoryItem::new( + "Project Shared Notification", + window_container(400., 72.).child( + CollabNotification::new( + "https://avatars.githubusercontent.com/u/1714999?v=4", + Button::new("open", "Open"), + Button::new("dismiss", "Dismiss"), + ) + .child(Label::new("iamnbutler")) + .child(Label::new("is sharing a project in Zed:")) + .child(Label::new("zed")), + ), + )), + ) + } +} diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index 033b3fa8d9077ee9866f0a0a7e382d5f68d3e483..9f08556757b6b59d1aed6fa7361029a36664a40a 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -14,6 +14,7 @@ anyhow.workspace = true backtrace-on-stack-overflow = "0.3.0" chrono = "0.4" clap = { version = "4.4", features = ["derive", "string"] } +collab_ui = { path = "../collab_ui", features = ["stories"] } strum = { version = "0.25.0", features = ["derive"] } dialoguer = { version = "0.11.0", features = ["fuzzy-select"] } editor = { path = "../editor" } diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs index 27ddfe26ac2884cd327f065c01bdf8bf7617e624..120e60d34a1499ed143394713e0c46fbf1bc2dfa 100644 --- a/crates/storybook/src/story_selector.rs +++ b/crates/storybook/src/story_selector.rs @@ -16,6 +16,7 @@ pub enum ComponentStory { Avatar, Button, Checkbox, + CollabNotification, ContextMenu, Cursor, Disclosure, @@ -45,6 +46,9 @@ impl ComponentStory { Self::Avatar => cx.new_view(|_| ui::AvatarStory).into(), Self::Button => cx.new_view(|_| ui::ButtonStory).into(), Self::Checkbox => cx.new_view(|_| ui::CheckboxStory).into(), + Self::CollabNotification => cx + .new_view(|_| collab_ui::notifications::CollabNotificationStory) + .into(), Self::ContextMenu => cx.new_view(|_| ui::ContextMenuStory).into(), Self::Cursor => cx.new_view(|_| crate::stories::CursorStory).into(), Self::Disclosure => cx.new_view(|_| ui::DisclosureStory).into(), From aa1d2d2f24c666536d6d57599cdabcf6238b85cd Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 9 Jan 2024 10:08:27 +0200 Subject: [PATCH 49/54] Remove dbg! usage from tests --- .../src/syntax_map/syntax_map_tests.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/language/src/syntax_map/syntax_map_tests.rs b/crates/language/src/syntax_map/syntax_map_tests.rs index f20f481613eabfffddee791873d78d6383086ade..8b9169d1cce47f96cd9fb6a768d5f3b5397ed4d4 100644 --- a/crates/language/src/syntax_map/syntax_map_tests.rs +++ b/crates/language/src/syntax_map/syntax_map_tests.rs @@ -258,19 +258,19 @@ fn test_typing_multiple_new_injections() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ - "fn a() { dbg }", - "fn a() { dbg«!» }", - "fn a() { dbg!«()» }", - "fn a() { dbg!(«b») }", - "fn a() { dbg!(b«.») }", - "fn a() { dbg!(b.«c») }", - "fn a() { dbg!(b.c«()») }", - "fn a() { dbg!(b.c(«vec»)) }", - "fn a() { dbg!(b.c(vec«!»)) }", - "fn a() { dbg!(b.c(vec!«[]»)) }", - "fn a() { dbg!(b.c(vec![«d»])) }", - "fn a() { dbg!(b.c(vec![d«.»])) }", - "fn a() { dbg!(b.c(vec![d.«e»])) }", + "fn a() { test_macro }", + "fn a() { test_macro«!» }", + "fn a() { test_macro!«()» }", + "fn a() { test_macro!(«b») }", + "fn a() { test_macro!(b«.») }", + "fn a() { test_macro!(b.«c») }", + "fn a() { test_macro!(b.c«()») }", + "fn a() { test_macro!(b.c(«vec»)) }", + "fn a() { test_macro!(b.c(vec«!»)) }", + "fn a() { test_macro!(b.c(vec!«[]»)) }", + "fn a() { test_macro!(b.c(vec![«d»])) }", + "fn a() { test_macro!(b.c(vec![d«.»])) }", + "fn a() { test_macro!(b.c(vec![d.«e»])) }", ], ); @@ -278,7 +278,7 @@ fn test_typing_multiple_new_injections() { &syntax_map, &buffer, &["field"], - "fn a() { dbg!(b.«c»(vec![d.«e»])) }", + "fn a() { test_macro!(b.«c»(vec![d.«e»])) }", ); } From 625c9d89802fa580ca1860975a6ddd53f13656ff Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 9 Jan 2024 10:13:40 +0200 Subject: [PATCH 50/54] Remove some todo!'s --- crates/editor/src/test.rs | 6 +- crates/gpui/docs/key_dispatch.md | 4 +- crates/gpui/src/action.rs | 1 - crates/gpui/src/app/test_context.rs | 5 +- crates/gpui/src/elements/overlay.rs | 2 +- crates/gpui/src/platform/test/display.rs | 2 +- crates/gpui/src/platform/test/platform.rs | 1 - crates/gpui/tests/action_macros.rs | 12 +-- crates/workspace/src/dock.rs | 1 - crates/workspace/src/notifications.rs | 99 ----------------------- crates/workspace/src/pane_group.rs | 3 +- crates/workspace/src/toolbar.rs | 83 ------------------- crates/workspace/src/workspace.rs | 9 +-- crates/zed/src/zed.rs | 1 - 14 files changed, 18 insertions(+), 211 deletions(-) diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 4ce539ad797b2a3064a57ca15ff8e97b0f3c307e..d3337db2581b0bc7148059457cc9eef6943cc8d1 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -60,8 +60,7 @@ pub fn assert_text_with_selections( #[allow(dead_code)] #[cfg(any(test, feature = "test-support"))] pub(crate) fn build_editor(buffer: Model, cx: &mut ViewContext) -> Editor { - // todo!() - Editor::new(EditorMode::Full, buffer, None, /*None,*/ cx) + Editor::new(EditorMode::Full, buffer, None, cx) } pub(crate) fn build_editor_with_project( @@ -69,6 +68,5 @@ pub(crate) fn build_editor_with_project( buffer: Model, cx: &mut ViewContext, ) -> Editor { - // todo!() - Editor::new(EditorMode::Full, buffer, Some(project), /*None,*/ cx) + Editor::new(EditorMode::Full, buffer, Some(project), cx) } diff --git a/crates/gpui/docs/key_dispatch.md b/crates/gpui/docs/key_dispatch.md index daf6f820cd5c98f45415c071d38518d55b269d2f..804a0b576194b2be4f23852a49369d988b9d81cb 100644 --- a/crates/gpui/docs/key_dispatch.md +++ b/crates/gpui/docs/key_dispatch.md @@ -50,7 +50,7 @@ impl Render for Menu { .on_action(|this, move: &MoveDown, cx| { // ... }) - .children(todo!()) + .children(unimplemented!()) } } ``` @@ -68,7 +68,7 @@ impl Render for Menu { .on_action(|this, move: &MoveDown, cx| { // ... }) - .children(todo!()) + .children(unimplemented!()) } } ``` diff --git a/crates/gpui/src/action.rs b/crates/gpui/src/action.rs index e335c4255e4deb0d6c5720b03ce017952a6fa229..81b392087aeb8f80571b7bb379948b1b886fe793 100644 --- a/crates/gpui/src/action.rs +++ b/crates/gpui/src/action.rs @@ -203,7 +203,6 @@ macro_rules! __impl_action { ) } - // todo!() why is this needed in addition to name? fn debug_name() -> &'static str where Self: ::std::marker::Sized diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 0f71ea61a9ec347b01e601f5e3c1a2237b80e691..a530aaf69b94ce65d35feee015738f306b3a79c3 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -467,12 +467,11 @@ impl View { } } - // todo!(start_waiting) - // cx.borrow().foreground_executor().start_waiting(); + cx.borrow().background_executor().start_waiting(); rx.recv() .await .expect("view dropped with pending condition"); - // cx.borrow().foreground_executor().finish_waiting(); + cx.borrow().background_executor().finish_waiting(); } }) .await diff --git a/crates/gpui/src/elements/overlay.rs b/crates/gpui/src/elements/overlay.rs index eab3ee60b41ded0f39fa548285b3d6be572e2ba1..6772baa2f9c117a834a716efe4d5e733948ce3b3 100644 --- a/crates/gpui/src/elements/overlay.rs +++ b/crates/gpui/src/elements/overlay.rs @@ -14,8 +14,8 @@ pub struct Overlay { children: SmallVec<[AnyElement; 2]>, anchor_corner: AnchorCorner, fit_mode: OverlayFitMode, - // todo!(); anchor_position: Option>, + // todo!(); // position_mode: OverlayPositionMode, } diff --git a/crates/gpui/src/platform/test/display.rs b/crates/gpui/src/platform/test/display.rs index 95f1daf8e92fc8bc620a91d3c1aa1ed12818c384..68dbb0fdf3466b9f3a2800bf68768aea2f5dd1e2 100644 --- a/crates/gpui/src/platform/test/display.rs +++ b/crates/gpui/src/platform/test/display.rs @@ -32,7 +32,7 @@ impl PlatformDisplay for TestDisplay { } fn as_any(&self) -> &dyn std::any::Any { - todo!() + unimplemented!() } fn bounds(&self) -> crate::Bounds { diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 695323e9c46b8e2a8f4260a682d8e214f58c43f4..ec3d7a0ff008ee64c92e8ceece0a2ef32cacff14 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -103,7 +103,6 @@ impl TestPlatform { } } -// todo!("implement out what our tests needed in GPUI 1") impl Platform for TestPlatform { fn background_executor(&self) -> BackgroundExecutor { self.background_executor.clone() diff --git a/crates/gpui/tests/action_macros.rs b/crates/gpui/tests/action_macros.rs index 9e5f6dea16ca6ad0ad7a1eb7f7098b61e0fd4cea..99572a4b3c5b1c84a4aa65fb95c72b25b2aef9ec 100644 --- a/crates/gpui/tests/action_macros.rs +++ b/crates/gpui/tests/action_macros.rs @@ -18,33 +18,33 @@ fn test_action_macros() { impl gpui::Action for RegisterableAction { fn boxed_clone(&self) -> Box { - todo!() + unimplemented!() } fn as_any(&self) -> &dyn std::any::Any { - todo!() + unimplemented!() } fn partial_eq(&self, _action: &dyn gpui::Action) -> bool { - todo!() + unimplemented!() } fn name(&self) -> &str { - todo!() + unimplemented!() } fn debug_name() -> &'static str where Self: Sized, { - todo!() + unimplemented!() } fn build(_value: serde_json::Value) -> anyhow::Result> where Self: Sized, { - todo!() + unimplemented!() } } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index c13a00b11c897b46ef5e2ac69ae10848c573ebf2..adc4fb5c80b2fd35af7ae32a620a550c977e469a 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -395,7 +395,6 @@ impl Dock { }) .ok(); } - // todo!() we do not use this event in the production code (even in zed1), remove it PanelEvent::Activate => { if let Some(ix) = this .panel_entries diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 394772b9c45b0a31bf36260389ee2e2fcc7ec717..d70de8c13dc7b45522952474506d609e1a75788b 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -247,105 +247,6 @@ pub mod simple_message_notification { })) } } - // todo!() - // impl View for MessageNotification { - // fn ui_name() -> &'static str { - // "MessageNotification" - // } - - // fn render(&mut self, cx: &mut gpui::ViewContext) -> gpui::AnyElement { - // let theme = theme::current(cx).clone(); - // let theme = &theme.simple_message_notification; - - // enum MessageNotificationTag {} - - // let click_message = self.click_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(); - - // Flex::column() - // .with_child( - // Flex::row() - // .with_child( - // message - // .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); - // Svg::new("icons/x.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); - - // Flex::row() - // .with_child( - // Text::new(click_message, style.text.clone()) - // .contained() - // .with_style(style.container), - // ) - // .contained() - // }, - // ) - // .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() - // }) - // .into_any() - // } - // } } pub trait NotifyResultExt { diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 68fd3c084f591e8bd7401c66e056e38beada1278..e28d0e63deda2a97ac3c4a9cd77d4beebb802333 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -12,7 +12,7 @@ use serde::Deserialize; use std::sync::Arc; use ui::{prelude::*, Button}; -const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4) +const HANDLE_HITBOX_SIZE: f32 = 4.0; const HORIZONTAL_MIN_SIZE: f32 = 80.; const VERTICAL_MIN_SIZE: f32 = 100.; @@ -707,7 +707,6 @@ mod element { proposed_current_pixel_change -= current_pixel_change; } - // todo!(schedule serialize) workspace .update(cx, |this, cx| this.schedule_serialize(cx)) .log_err(); diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index dc17cd3c1956b6f8475f04c6478ab86a86eb5689..cc072b08b9eac0cf13039dd45216483018d5701a 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -133,82 +133,6 @@ impl Render for Toolbar { } } -// todo!() -// impl View for Toolbar { -// fn ui_name() -> &'static str { -// "Toolbar" -// } - -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// let theme = &theme::current(cx).workspace.toolbar; - -// let mut primary_left_items = Vec::new(); -// let mut primary_right_items = Vec::new(); -// let mut secondary_item = None; -// let spacing = theme.item_spacing; -// let mut primary_items_row_count = 1; - -// for (item, position) in &self.items { -// match *position { -// ToolbarItemLocation::Hidden => {} - -// ToolbarItemLocation::PrimaryLeft { flex } => { -// primary_items_row_count = primary_items_row_count.max(item.row_count(cx)); -// let left_item = ChildView::new(item.as_any(), cx).aligned(); -// if let Some((flex, expanded)) = flex { -// primary_left_items.push(left_item.flex(flex, expanded).into_any()); -// } else { -// primary_left_items.push(left_item.into_any()); -// } -// } - -// ToolbarItemLocation::PrimaryRight { flex } => { -// primary_items_row_count = primary_items_row_count.max(item.row_count(cx)); -// let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float(); -// if let Some((flex, expanded)) = flex { -// primary_right_items.push(right_item.flex(flex, expanded).into_any()); -// } else { -// primary_right_items.push(right_item.into_any()); -// } -// } - -// ToolbarItemLocation::Secondary => { -// secondary_item = Some( -// ChildView::new(item.as_any(), cx) -// .constrained() -// .with_height(theme.height * item.row_count(cx) as f32) -// .into_any(), -// ); -// } -// } -// } - -// let container_style = theme.container; -// let height = theme.height * primary_items_row_count as f32; - -// let mut primary_items = Flex::row().with_spacing(spacing); -// primary_items.extend(primary_left_items); -// primary_items.extend(primary_right_items); - -// let mut toolbar = Flex::column(); -// if !primary_items.is_empty() { -// toolbar.add_child(primary_items.constrained().with_height(height)); -// } -// if let Some(secondary_item) = secondary_item { -// toolbar.add_child(secondary_item); -// } - -// if toolbar.is_empty() { -// toolbar.into_any_named("toolbar") -// } else { -// toolbar -// .contained() -// .with_style(container_style) -// .into_any_named("toolbar") -// } -// } -// } - impl Toolbar { pub fn new() -> Self { Self { @@ -312,10 +236,3 @@ impl ToolbarItemViewHandle for View { self.read(cx).row_count(cx) } } - -// todo!() -// impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle { -// fn from(val: &dyn ToolbarItemViewHandle) -> Self { -// val.as_any().clone() -// } -// } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ad02637ae39795c0e61ee38074062c518d12a3eb..739ce88636ca32a88be1496103ad03ed42f63808 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -943,10 +943,8 @@ impl Workspace { cx: &mut ViewContext, ) -> Task> { let to_load = if let Some(pane) = pane.upgrade() { - // todo!("focus") - // cx.focus(&pane); - pane.update(cx, |pane, cx| { + pane.focus(cx); loop { // Retrieve the weak item handle from the history. let entry = pane.nav_history_mut().pop(mode, cx)?; @@ -1631,8 +1629,7 @@ impl Workspace { }); } - // todo!("focus") - // cx.focus_self(); + cx.focus_self(); cx.notify(); self.serialize_workspace(cx); } @@ -1713,6 +1710,7 @@ impl Workspace { cx.notify(); } + // todo!() // #[cfg(any(test, feature = "test-support"))] // pub fn zoomed_view(&self, cx: &AppContext) -> Option { // self.zoomed.and_then(|view| view.upgrade(cx)) @@ -2992,7 +2990,6 @@ impl Workspace { cx.notify(); } - #[allow(unused)] fn schedule_serialize(&mut self, cx: &mut ViewContext) { self._schedule_serialize = Some(cx.spawn(|this, mut cx| async move { cx.background_executor() diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 73368a988377e8edb927fb1d629c78385112a492..0b90961d2352a31e80f38dbd3eda86df4055e788 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -764,7 +764,6 @@ fn open_bundled_file( .detach_and_log_err(cx); } -// todo!() #[cfg(test)] mod tests { use super::*; From 29ed067b2651c881a99c9c3be9f1d3e523911d0c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:20:52 +0100 Subject: [PATCH 51/54] Add a missing default value to docs (#3973) Release Notes: - N/A --- crates/call/src/call_settings.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index 88c3fe84ce118e011e729e335cf8631ff273e014..441323ad5ffcf4d6d525525139905278bd15ca0b 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -9,9 +9,12 @@ pub struct CallSettings { pub mute_on_join: bool, } +/// Configuration of voice calls in Zed. #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct CallSettingsContent { /// Whether the microphone should be muted when joining a channel or a call. + /// + /// Default: false pub mute_on_join: Option, } From fa53353c575897b01e04eeeddb90d4e16729a700 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 9 Jan 2024 10:11:20 -0500 Subject: [PATCH 52/54] Rename `IconElement` to just `Icon` (#3974) This PR renames the `IconElement` component to just `Icon`. This better matches the rest of our components, as `IconElement` was the only one using this naming convention. The `Icon` enum has been renamed to `IconName` to free up the name. I was trying to come up with a way that would allow rendering an `Icon::Zed` directly (and thus make the `IconElement` a hidden part of the API), but I couldn't come up with a way to do this cleanly. Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 34 +-- crates/auto_update/src/update_notification.rs | 4 +- crates/collab_ui/src/chat_panel.rs | 14 +- crates/collab_ui/src/collab_panel.rs | 62 +++--- .../src/collab_panel/channel_modal.rs | 4 +- .../src/collab_panel/contact_finder.rs | 4 +- crates/collab_ui/src/collab_titlebar_item.rs | 20 +- crates/collab_ui/src/notification_panel.rs | 12 +- crates/copilot_ui/src/copilot_button.rs | 12 +- crates/copilot_ui/src/sign_in.rs | 4 +- crates/diagnostics/src/diagnostics.rs | 12 +- crates/diagnostics/src/items.rs | 14 +- crates/diagnostics/src/toolbar_controls.rs | 4 +- crates/editor/src/editor.rs | 12 +- crates/feedback/src/deploy_feedback_button.rs | 4 +- crates/feedback/src/feedback_modal.rs | 2 +- crates/project_panel/src/project_panel.rs | 10 +- .../quick_action_bar/src/quick_action_bar.rs | 12 +- crates/search/src/buffer_search.rs | 16 +- crates/search/src/project_search.rs | 24 +-- crates/search/src/search.rs | 8 +- crates/search/src/search_bar.rs | 2 +- crates/terminal_view/src/terminal_panel.rs | 12 +- crates/terminal_view/src/terminal_view.rs | 4 +- crates/ui/src/components/button/button.rs | 10 +- .../ui/src/components/button/button_icon.rs | 12 +- .../ui/src/components/button/icon_button.rs | 10 +- crates/ui/src/components/checkbox.rs | 6 +- crates/ui/src/components/context_menu.rs | 10 +- crates/ui/src/components/disclosure.rs | 6 +- crates/ui/src/components/icon.rs | 202 +++++++++--------- crates/ui/src/components/keybinding.rs | 44 ++-- .../ui/src/components/list/list_sub_header.rs | 15 +- crates/ui/src/components/stories/button.rs | 6 +- crates/ui/src/components/stories/icon.rs | 8 +- .../ui/src/components/stories/icon_button.rs | 18 +- .../ui/src/components/stories/list_header.rs | 14 +- crates/ui/src/components/stories/list_item.rs | 8 +- crates/ui/src/components/stories/tab.rs | 2 +- crates/ui/src/components/stories/tab_bar.rs | 11 +- crates/ui/src/prelude.rs | 2 +- crates/workspace/src/dock.rs | 8 +- crates/workspace/src/notifications.rs | 4 +- crates/workspace/src/pane.rs | 18 +- crates/workspace/src/shared_screen.rs | 4 +- 45 files changed, 364 insertions(+), 360 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index f53343531af09083999e04f1d3dce92eafe34850..d4743afb714ab47be3e38b196a45174fb33c2aa5 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -933,7 +933,7 @@ impl AssistantPanel { } fn render_hamburger_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("hamburger_button", Icon::Menu) + IconButton::new("hamburger_button", IconName::Menu) .on_click(cx.listener(|this, _event, cx| { if this.active_editor().is_some() { this.set_active_editor_index(None, cx); @@ -957,7 +957,7 @@ impl AssistantPanel { } fn render_split_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("split_button", Icon::Snip) + IconButton::new("split_button", IconName::Snip) .on_click(cx.listener(|this, _event, cx| { if let Some(active_editor) = this.active_editor() { active_editor.update(cx, |editor, cx| editor.split(&Default::default(), cx)); @@ -968,7 +968,7 @@ impl AssistantPanel { } fn render_assist_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("assist_button", Icon::MagicWand) + IconButton::new("assist_button", IconName::MagicWand) .on_click(cx.listener(|this, _event, cx| { if let Some(active_editor) = this.active_editor() { active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx)); @@ -979,7 +979,7 @@ impl AssistantPanel { } fn render_quote_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("quote_button", Icon::Quote) + IconButton::new("quote_button", IconName::Quote) .on_click(cx.listener(|this, _event, cx| { if let Some(workspace) = this.workspace.upgrade() { cx.window_context().defer(move |cx| { @@ -994,7 +994,7 @@ impl AssistantPanel { } fn render_plus_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("plus_button", Icon::Plus) + IconButton::new("plus_button", IconName::Plus) .on_click(cx.listener(|this, _event, cx| { this.new_conversation(cx); })) @@ -1004,12 +1004,12 @@ impl AssistantPanel { fn render_zoom_button(&self, cx: &mut ViewContext) -> impl IntoElement { let zoomed = self.zoomed; - IconButton::new("zoom_button", Icon::Maximize) + IconButton::new("zoom_button", IconName::Maximize) .on_click(cx.listener(|this, _event, cx| { this.toggle_zoom(&ToggleZoom, cx); })) .selected(zoomed) - .selected_icon(Icon::Minimize) + .selected_icon(IconName::Minimize) .icon_size(IconSize::Small) .tooltip(move |cx| { Tooltip::for_action(if zoomed { "Zoom Out" } else { "Zoom In" }, &ToggleZoom, cx) @@ -1286,8 +1286,8 @@ impl Panel for AssistantPanel { } } - fn icon(&self, cx: &WindowContext) -> Option { - Some(Icon::Ai).filter(|_| AssistantSettings::get_global(cx).button) + fn icon(&self, cx: &WindowContext) -> Option { + Some(IconName::Ai).filter(|_| AssistantSettings::get_global(cx).button) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { @@ -2349,7 +2349,7 @@ impl ConversationEditor { div() .id("error") .tooltip(move |cx| Tooltip::text(error.clone(), cx)) - .child(IconElement::new(Icon::XCircle)), + .child(Icon::new(IconName::XCircle)), ) } else { None @@ -2645,7 +2645,7 @@ impl Render for InlineAssistant { .justify_center() .w(measurements.gutter_width) .child( - IconButton::new("include_conversation", Icon::Ai) + IconButton::new("include_conversation", IconName::Ai) .on_click(cx.listener(|this, _, cx| { this.toggle_include_conversation(&ToggleIncludeConversation, cx) })) @@ -2660,7 +2660,7 @@ impl Render for InlineAssistant { ) .children(if SemanticIndex::enabled(cx) { Some( - IconButton::new("retrieve_context", Icon::MagnifyingGlass) + IconButton::new("retrieve_context", IconName::MagnifyingGlass) .on_click(cx.listener(|this, _, cx| { this.toggle_retrieve_context(&ToggleRetrieveContext, cx) })) @@ -2682,7 +2682,7 @@ impl Render for InlineAssistant { div() .id("error") .tooltip(move |cx| Tooltip::text(error_message.clone(), cx)) - .child(IconElement::new(Icon::XCircle).color(Color::Error)), + .child(Icon::new(IconName::XCircle).color(Color::Error)), ) } else { None @@ -2957,7 +2957,7 @@ impl InlineAssistant { div() .id("error") .tooltip(|cx| Tooltip::text("Not Authenticated. Please ensure you have a valid 'OPENAI_API_KEY' in your environment variables.", cx)) - .child(IconElement::new(Icon::XCircle)) + .child(Icon::new(IconName::XCircle)) .into_any_element() ), @@ -2965,7 +2965,7 @@ impl InlineAssistant { div() .id("error") .tooltip(|cx| Tooltip::text("Not Indexed", cx)) - .child(IconElement::new(Icon::XCircle)) + .child(Icon::new(IconName::XCircle)) .into_any_element() ), @@ -2996,7 +2996,7 @@ impl InlineAssistant { div() .id("update") .tooltip(move |cx| Tooltip::text(status_text.clone(), cx)) - .child(IconElement::new(Icon::Update).color(Color::Info)) + .child(Icon::new(IconName::Update).color(Color::Info)) .into_any_element() ) } @@ -3005,7 +3005,7 @@ impl InlineAssistant { div() .id("check") .tooltip(|cx| Tooltip::text("Index up to date", cx)) - .child(IconElement::new(Icon::Check).color(Color::Success)) + .child(Icon::new(IconName::Check).color(Color::Success)) .into_any_element() ), } diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs index f00172591ecf37221e34cf4ff3b5b330b72fba28..65f786bca4c4e1a729974459fbdf8e549a9893cc 100644 --- a/crates/auto_update/src/update_notification.rs +++ b/crates/auto_update/src/update_notification.rs @@ -4,7 +4,7 @@ use gpui::{ }; use menu::Cancel; use util::channel::ReleaseChannel; -use workspace::ui::{h_stack, v_stack, Icon, IconElement, Label, StyledExt}; +use workspace::ui::{h_stack, v_stack, Icon, IconName, Label, StyledExt}; pub struct UpdateNotification { version: SemanticVersion, @@ -30,7 +30,7 @@ impl Render for UpdateNotification { .child( div() .id("cancel") - .child(IconElement::new(Icon::Close)) + .child(Icon::new(IconName::Close)) .cursor_pointer() .on_click(cx.listener(|this, _, cx| this.dismiss(&menu::Cancel, cx))), ), diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 4f8e12f1e8ea2be3ca2d5354a80e684e94269c22..5786ab10d4ca59b998b1b16ea7bb3c53611b4399 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; -use ui::{prelude::*, Avatar, Button, Icon, IconButton, Label, TabBar, Tooltip}; +use ui::{prelude::*, Avatar, Button, IconButton, IconName, Label, TabBar, Tooltip}; use util::{ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel, PanelEvent}, @@ -281,12 +281,12 @@ impl ChatPanel { )), ) .end_child( - IconButton::new("notes", Icon::File) + IconButton::new("notes", IconName::File) .on_click(cx.listener(Self::open_notes)) .tooltip(|cx| Tooltip::text("Open notes", cx)), ) .end_child( - IconButton::new("call", Icon::AudioOn) + IconButton::new("call", IconName::AudioOn) .on_click(cx.listener(Self::join_call)) .tooltip(|cx| Tooltip::text("Join call", cx)), ), @@ -395,7 +395,7 @@ impl ChatPanel { .w_8() .visible_on_hover("") .children(message_id_to_remove.map(|message_id| { - IconButton::new(("remove", message_id), Icon::XCircle).on_click( + IconButton::new(("remove", message_id), IconName::XCircle).on_click( cx.listener(move |this, _, cx| { this.remove_message(message_id, cx); }), @@ -429,7 +429,7 @@ impl ChatPanel { Button::new("sign-in", "Sign in") .style(ButtonStyle::Filled) .icon_color(Color::Muted) - .icon(Icon::Github) + .icon(IconName::Github) .icon_position(IconPosition::Start) .full_width() .on_click(cx.listener(move |this, _, cx| { @@ -622,12 +622,12 @@ impl Panel for ChatPanel { "ChatPanel" } - fn icon(&self, cx: &WindowContext) -> Option { + fn icon(&self, cx: &WindowContext) -> Option { if !is_channels_feature_enabled(cx) { return None; } - Some(ui::Icon::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button) + Some(ui::IconName::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 86d0131d70afa9dbf0a9c51d6943dcb1f1ad2f80..df8f2a251fdf4d86ff05f0eaa8052c34317435bb 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -31,7 +31,7 @@ use smallvec::SmallVec; use std::{mem, sync::Arc}; use theme::{ActiveTheme, ThemeSettings}; use ui::{ - prelude::*, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize, Label, + prelude::*, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconName, IconSize, Label, ListHeader, ListItem, Tooltip, }; use util::{maybe, ResultExt, TryFutureExt}; @@ -848,7 +848,7 @@ impl CollabPanel { .end_slot(if is_pending { Label::new("Calling").color(Color::Muted).into_any_element() } else if is_current_user { - IconButton::new("leave-call", Icon::Exit) + IconButton::new("leave-call", IconName::Exit) .style(ButtonStyle::Subtle) .on_click(move |_, cx| Self::leave_call(cx)) .tooltip(|cx| Tooltip::text("Leave Call", cx)) @@ -897,7 +897,7 @@ impl CollabPanel { h_stack() .gap_1() .child(render_tree_branch(is_last, false, cx)) - .child(IconButton::new(0, Icon::Folder)), + .child(IconButton::new(0, IconName::Folder)), ) .child(Label::new(project_name.clone())) .tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx)) @@ -918,7 +918,7 @@ impl CollabPanel { h_stack() .gap_1() .child(render_tree_branch(is_last, false, cx)) - .child(IconButton::new(0, Icon::Screen)), + .child(IconButton::new(0, IconName::Screen)), ) .child(Label::new("Screen")) .when_some(peer_id, |this, _| { @@ -959,7 +959,7 @@ impl CollabPanel { h_stack() .gap_1() .child(render_tree_branch(false, true, cx)) - .child(IconButton::new(0, Icon::File)), + .child(IconButton::new(0, IconName::File)), ) .child(div().h_7().w_full().child(Label::new("notes"))) .tooltip(move |cx| Tooltip::text("Open Channel Notes", cx)) @@ -980,7 +980,7 @@ impl CollabPanel { h_stack() .gap_1() .child(render_tree_branch(false, false, cx)) - .child(IconButton::new(0, Icon::MessageBubbles)), + .child(IconButton::new(0, IconName::MessageBubbles)), ) .child(Label::new("chat")) .tooltip(move |cx| Tooltip::text("Open Chat", cx)) @@ -1724,7 +1724,7 @@ impl CollabPanel { .child( Button::new("sign_in", "Sign in") .icon_color(Color::Muted) - .icon(Icon::Github) + .icon(IconName::Github) .icon_position(IconPosition::Start) .style(ButtonStyle::Filled) .full_width() @@ -1921,7 +1921,7 @@ impl CollabPanel { let button = match section { Section::ActiveCall => channel_link.map(|channel_link| { let channel_link_copy = channel_link.clone(); - IconButton::new("channel-link", Icon::Copy) + IconButton::new("channel-link", IconName::Copy) .icon_size(IconSize::Small) .size(ButtonSize::None) .visible_on_hover("section-header") @@ -1933,13 +1933,13 @@ impl CollabPanel { .into_any_element() }), Section::Contacts => Some( - IconButton::new("add-contact", Icon::Plus) + IconButton::new("add-contact", IconName::Plus) .on_click(cx.listener(|this, _, cx| this.toggle_contact_finder(cx))) .tooltip(|cx| Tooltip::text("Search for new contact", cx)) .into_any_element(), ), Section::Channels => Some( - IconButton::new("add-channel", Icon::Plus) + IconButton::new("add-channel", IconName::Plus) .on_click(cx.listener(|this, _, cx| this.new_root_channel(cx))) .tooltip(|cx| Tooltip::text("Create a channel", cx)) .into_any_element(), @@ -2010,7 +2010,7 @@ impl CollabPanel { }) .when(!calling, |el| { el.child( - IconButton::new("remove_contact", Icon::Close) + IconButton::new("remove_contact", IconName::Close) .icon_color(Color::Muted) .visible_on_hover("") .tooltip(|cx| Tooltip::text("Remove Contact", cx)) @@ -2071,13 +2071,13 @@ impl CollabPanel { let controls = if is_incoming { vec![ - IconButton::new("decline-contact", Icon::Close) + IconButton::new("decline-contact", IconName::Close) .on_click(cx.listener(move |this, _, cx| { this.respond_to_contact_request(user_id, false, cx); })) .icon_color(color) .tooltip(|cx| Tooltip::text("Decline invite", cx)), - IconButton::new("accept-contact", Icon::Check) + IconButton::new("accept-contact", IconName::Check) .on_click(cx.listener(move |this, _, cx| { this.respond_to_contact_request(user_id, true, cx); })) @@ -2086,7 +2086,7 @@ impl CollabPanel { ] } else { let github_login = github_login.clone(); - vec![IconButton::new("remove_contact", Icon::Close) + vec![IconButton::new("remove_contact", IconName::Close) .on_click(cx.listener(move |this, _, cx| { this.remove_contact(user_id, &github_login, cx); })) @@ -2126,13 +2126,13 @@ impl CollabPanel { }; let controls = [ - IconButton::new("reject-invite", Icon::Close) + IconButton::new("reject-invite", IconName::Close) .on_click(cx.listener(move |this, _, cx| { this.respond_to_channel_invite(channel_id, false, cx); })) .icon_color(color) .tooltip(|cx| Tooltip::text("Decline invite", cx)), - IconButton::new("accept-invite", Icon::Check) + IconButton::new("accept-invite", IconName::Check) .on_click(cx.listener(move |this, _, cx| { this.respond_to_channel_invite(channel_id, true, cx); })) @@ -2150,7 +2150,7 @@ impl CollabPanel { .child(h_stack().children(controls)), ) .start_slot( - IconElement::new(Icon::Hash) + Icon::new(IconName::Hash) .size(IconSize::Small) .color(Color::Muted), ) @@ -2162,7 +2162,7 @@ impl CollabPanel { cx: &mut ViewContext, ) -> ListItem { ListItem::new("contact-placeholder") - .child(IconElement::new(Icon::Plus)) + .child(Icon::new(IconName::Plus)) .child(Label::new("Add a Contact")) .selected(is_selected) .on_click(cx.listener(|this, _, cx| this.toggle_contact_finder(cx))) @@ -2246,7 +2246,7 @@ impl CollabPanel { }; let messages_button = |cx: &mut ViewContext| { - IconButton::new("channel_chat", Icon::MessageBubbles) + IconButton::new("channel_chat", IconName::MessageBubbles) .icon_size(IconSize::Small) .icon_color(if has_messages_notification { Color::Default @@ -2258,7 +2258,7 @@ impl CollabPanel { }; let notes_button = |cx: &mut ViewContext| { - IconButton::new("channel_notes", Icon::File) + IconButton::new("channel_notes", IconName::File) .icon_size(IconSize::Small) .icon_color(if has_notes_notification { Color::Default @@ -2315,9 +2315,13 @@ impl CollabPanel { }, )) .start_slot( - IconElement::new(if is_public { Icon::Public } else { Icon::Hash }) - .size(IconSize::Small) - .color(Color::Muted), + Icon::new(if is_public { + IconName::Public + } else { + IconName::Hash + }) + .size(IconSize::Small) + .color(Color::Muted), ) .child( h_stack() @@ -2386,7 +2390,7 @@ impl CollabPanel { .indent_level(depth + 1) .indent_step_size(px(20.)) .start_slot( - IconElement::new(Icon::Hash) + Icon::new(IconName::Hash) .size(IconSize::Small) .color(Color::Muted), ); @@ -2500,10 +2504,10 @@ impl Panel for CollabPanel { cx.notify(); } - fn icon(&self, cx: &gpui::WindowContext) -> Option { + fn icon(&self, cx: &gpui::WindowContext) -> Option { CollaborationPanelSettings::get_global(cx) .button - .then(|| ui::Icon::Collab) + .then(|| ui::IconName::Collab) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { @@ -2646,11 +2650,11 @@ impl Render for DraggedChannelView { .p_1() .gap_1() .child( - IconElement::new( + Icon::new( if self.channel.visibility == proto::ChannelVisibility::Public { - Icon::Public + IconName::Public } else { - Icon::Hash + IconName::Hash }, ) .size(IconSize::Small) diff --git a/crates/collab_ui/src/collab_panel/channel_modal.rs b/crates/collab_ui/src/collab_panel/channel_modal.rs index f3ae16f7939910cef5d79581725003c7bfe1c987..8020613c1ae2a4cc2ac9e0ad61293541c451f0aa 100644 --- a/crates/collab_ui/src/collab_panel/channel_modal.rs +++ b/crates/collab_ui/src/collab_panel/channel_modal.rs @@ -168,7 +168,7 @@ impl Render for ChannelModal { .w_px() .flex_1() .gap_1() - .child(IconElement::new(Icon::Hash).size(IconSize::Medium)) + .child(Icon::new(IconName::Hash).size(IconSize::Medium)) .child(Label::new(channel_name)), ) .child( @@ -406,7 +406,7 @@ impl PickerDelegate for ChannelModalDelegate { Some(ChannelRole::Guest) => Some(Label::new("Guest")), _ => None, }) - .child(IconButton::new("ellipsis", Icon::Ellipsis)) + .child(IconButton::new("ellipsis", IconName::Ellipsis)) .children( if let (Some((menu, _)), true) = (&self.context_menu, selected) { Some( diff --git a/crates/collab_ui/src/collab_panel/contact_finder.rs b/crates/collab_ui/src/collab_panel/contact_finder.rs index dbcacef7d645de6e30959b7b4d0b91b2f41aceed..b769ec7e7f394fb7a94c38f6220b507d43e77ba1 100644 --- a/crates/collab_ui/src/collab_panel/contact_finder.rs +++ b/crates/collab_ui/src/collab_panel/contact_finder.rs @@ -155,9 +155,7 @@ impl PickerDelegate for ContactFinderDelegate { .selected(selected) .start_slot(Avatar::new(user.avatar_uri.clone())) .child(Label::new(user.github_login.clone())) - .end_slot::( - icon_path.map(|icon_path| IconElement::from_path(icon_path)), - ), + .end_slot::(icon_path.map(|icon_path| Icon::from_path(icon_path))), ) } } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 6ccad2db0d107f4ee877ecdfd38563e880a79be5..f2106b9a8f4d769801d2eadc7c0259334966b8b6 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use theme::{ActiveTheme, PlayerColors}; use ui::{ h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, - IconButton, IconElement, TintColor, Tooltip, + IconButton, IconName, TintColor, Tooltip, }; use util::ResultExt; use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu}; @@ -213,7 +213,7 @@ impl Render for CollabTitlebarItem { .child( div() .child( - IconButton::new("leave-call", ui::Icon::Exit) + IconButton::new("leave-call", ui::IconName::Exit) .style(ButtonStyle::Subtle) .tooltip(|cx| Tooltip::text("Leave call", cx)) .icon_size(IconSize::Small) @@ -230,9 +230,9 @@ impl Render for CollabTitlebarItem { IconButton::new( "mute-microphone", if is_muted { - ui::Icon::MicMute + ui::IconName::MicMute } else { - ui::Icon::Mic + ui::IconName::Mic }, ) .tooltip(move |cx| { @@ -256,9 +256,9 @@ impl Render for CollabTitlebarItem { IconButton::new( "mute-sound", if is_deafened { - ui::Icon::AudioOff + ui::IconName::AudioOff } else { - ui::Icon::AudioOn + ui::IconName::AudioOn }, ) .style(ButtonStyle::Subtle) @@ -281,7 +281,7 @@ impl Render for CollabTitlebarItem { ) .when(!read_only, |this| { this.child( - IconButton::new("screen-share", ui::Icon::Screen) + IconButton::new("screen-share", ui::IconName::Screen) .style(ButtonStyle::Subtle) .icon_size(IconSize::Small) .selected(is_screen_sharing) @@ -573,7 +573,7 @@ impl CollabTitlebarItem { | client::Status::ReconnectionError { .. } => Some( div() .id("disconnected") - .child(IconElement::new(Icon::Disconnected).size(IconSize::Small)) + .child(Icon::new(IconName::Disconnected).size(IconSize::Small)) .tooltip(|cx| Tooltip::text("Disconnected", cx)) .into_any_element(), ), @@ -643,7 +643,7 @@ impl CollabTitlebarItem { h_stack() .gap_0p5() .child(Avatar::new(user.avatar_uri.clone())) - .child(IconElement::new(Icon::ChevronDown).color(Color::Muted)), + .child(Icon::new(IconName::ChevronDown).color(Color::Muted)), ) .style(ButtonStyle::Subtle) .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)), @@ -665,7 +665,7 @@ impl CollabTitlebarItem { .child( h_stack() .gap_0p5() - .child(IconElement::new(Icon::ChevronDown).color(Color::Muted)), + .child(Icon::new(IconName::ChevronDown).color(Color::Muted)), ) .style(ButtonStyle::Subtle) .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)), diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index e7c94984b229165aa26a43000221446d7b56e7a5..95473044a3f4242cd497ce0087fe1e47e8865d6f 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::{sync::Arc, time::Duration}; use time::{OffsetDateTime, UtcOffset}; -use ui::{h_stack, prelude::*, v_stack, Avatar, Button, Icon, IconButton, IconElement, Label}; +use ui::{h_stack, prelude::*, v_stack, Avatar, Button, Icon, IconButton, IconName, Label}; use util::{ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel, PanelEvent}, @@ -553,7 +553,7 @@ impl Render for NotificationPanel { .border_b_1() .border_color(cx.theme().colors().border) .child(Label::new("Notifications")) - .child(IconElement::new(Icon::Envelope)), + .child(Icon::new(IconName::Envelope)), ) .map(|this| { if self.client.user_id().is_none() { @@ -564,7 +564,7 @@ impl Render for NotificationPanel { .child( Button::new("sign_in_prompt_button", "Sign in") .icon_color(Color::Muted) - .icon(Icon::Github) + .icon(IconName::Github) .icon_position(IconPosition::Start) .style(ButtonStyle::Filled) .full_width() @@ -655,10 +655,10 @@ impl Panel for NotificationPanel { } } - fn icon(&self, cx: &gpui::WindowContext) -> Option { + fn icon(&self, cx: &gpui::WindowContext) -> Option { (NotificationPanelSettings::get_global(cx).button && self.notification_store.read(cx).notification_count() > 0) - .then(|| Icon::Bell) + .then(|| IconName::Bell) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { @@ -716,7 +716,7 @@ impl Render for NotificationToast { .children(user.map(|user| Avatar::new(user.avatar_uri.clone()))) .child(Label::new(self.text.clone())) .child( - IconButton::new("close", Icon::Close) + IconButton::new("close", IconName::Close) .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))), ) .on_click(cx.listener(|this, _, cx| { diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index e55f45c29333edbecc22742fccf0d4cd10a4a0df..e5a1a942358a20c72fbb1037413796aeb84be77a 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -17,7 +17,9 @@ use util::{paths, ResultExt}; use workspace::{ create_and_open_local_file, item::ItemHandle, - ui::{popover_menu, ButtonCommon, Clickable, ContextMenu, Icon, IconButton, IconSize, Tooltip}, + ui::{ + popover_menu, ButtonCommon, Clickable, ContextMenu, IconButton, IconName, IconSize, Tooltip, + }, StatusItemView, Toast, Workspace, }; use zed_actions::OpenBrowser; @@ -51,15 +53,15 @@ impl Render for CopilotButton { .unwrap_or_else(|| all_language_settings.copilot_enabled(None, None)); let icon = match status { - Status::Error(_) => Icon::CopilotError, + Status::Error(_) => IconName::CopilotError, Status::Authorized => { if enabled { - Icon::Copilot + IconName::Copilot } else { - Icon::CopilotDisabled + IconName::CopilotDisabled } } - _ => Icon::CopilotInit, + _ => IconName::CopilotInit, }; if let Status::Error(e) = status { diff --git a/crates/copilot_ui/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs index aeaa35784bfabe5ab75fc2b26a59dada83ddb61a..ba6f54b634a0e2f9f14ce296423fc905d40bf744 100644 --- a/crates/copilot_ui/src/sign_in.rs +++ b/crates/copilot_ui/src/sign_in.rs @@ -4,7 +4,7 @@ use gpui::{ FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, Subscription, ViewContext, }; -use ui::{prelude::*, Button, Icon, Label}; +use ui::{prelude::*, Button, IconName, Label}; use workspace::ModalView; const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; @@ -175,7 +175,7 @@ impl Render for CopilotCodeVerification { .w_32() .h_16() .flex_none() - .path(Icon::ZedXCopilot.path()) + .path(IconName::ZedXCopilot.path()) .text_color(cx.theme().colors().icon), ) .child(prompt) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 613fadf7f70caa6e35a7ecd739c8e16f6237be80..844a44c54f8bcf6eeae17ab3e629b4dee6e4a04b 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -36,7 +36,7 @@ use std::{ }; use theme::ActiveTheme; pub use toolbar_controls::ToolbarControls; -use ui::{h_stack, prelude::*, Icon, IconElement, Label}; +use ui::{h_stack, prelude::*, Icon, IconName, Label}; use util::TryFutureExt; use workspace::{ item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, @@ -660,7 +660,7 @@ impl Item for ProjectDiagnosticsEditor { then.child( h_stack() .gap_1() - .child(IconElement::new(Icon::XCircle).color(Color::Error)) + .child(Icon::new(IconName::XCircle).color(Color::Error)) .child(Label::new(self.summary.error_count.to_string()).color( if selected { Color::Default @@ -674,9 +674,7 @@ impl Item for ProjectDiagnosticsEditor { then.child( h_stack() .gap_1() - .child( - IconElement::new(Icon::ExclamationTriangle).color(Color::Warning), - ) + .child(Icon::new(IconName::ExclamationTriangle).color(Color::Warning)) .child(Label::new(self.summary.warning_count.to_string()).color( if selected { Color::Default @@ -816,10 +814,10 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { .flex_none() .map(|icon| { if diagnostic.severity == DiagnosticSeverity::ERROR { - icon.path(Icon::XCircle.path()) + icon.path(IconName::XCircle.path()) .text_color(Color::Error.color(cx)) } else { - icon.path(Icon::ExclamationTriangle.path()) + icon.path(IconName::ExclamationTriangle.path()) .text_color(Color::Warning.color(cx)) } }), diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 0c2d673d8e68b5bd43681788bde078442ed43d9d..035b84e1020048cd7c6d2cd107577b7c79786169 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -6,7 +6,7 @@ use gpui::{ }; use language::Diagnostic; use lsp::LanguageServerId; -use ui::{h_stack, prelude::*, Button, ButtonLike, Color, Icon, IconElement, Label, Tooltip}; +use ui::{h_stack, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip}; use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace}; use crate::{Deploy, ProjectDiagnosticsEditor}; @@ -25,7 +25,7 @@ impl Render for DiagnosticIndicator { let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) { (0, 0) => h_stack().map(|this| { this.child( - IconElement::new(Icon::Check) + Icon::new(IconName::Check) .size(IconSize::Small) .color(Color::Default), ) @@ -33,7 +33,7 @@ impl Render for DiagnosticIndicator { (0, warning_count) => h_stack() .gap_1() .child( - IconElement::new(Icon::ExclamationTriangle) + Icon::new(IconName::ExclamationTriangle) .size(IconSize::Small) .color(Color::Warning), ) @@ -41,7 +41,7 @@ impl Render for DiagnosticIndicator { (error_count, 0) => h_stack() .gap_1() .child( - IconElement::new(Icon::XCircle) + Icon::new(IconName::XCircle) .size(IconSize::Small) .color(Color::Error), ) @@ -49,13 +49,13 @@ impl Render for DiagnosticIndicator { (error_count, warning_count) => h_stack() .gap_1() .child( - IconElement::new(Icon::XCircle) + Icon::new(IconName::XCircle) .size(IconSize::Small) .color(Color::Error), ) .child(Label::new(error_count.to_string()).size(LabelSize::Small)) .child( - IconElement::new(Icon::ExclamationTriangle) + Icon::new(IconName::ExclamationTriangle) .size(IconSize::Small) .color(Color::Warning), ) @@ -66,7 +66,7 @@ impl Render for DiagnosticIndicator { Some( h_stack() .gap_2() - .child(IconElement::new(Icon::ArrowCircle).size(IconSize::Small)) + .child(Icon::new(IconName::ArrowCircle).size(IconSize::Small)) .child( Label::new("Checking…") .size(LabelSize::Small) diff --git a/crates/diagnostics/src/toolbar_controls.rs b/crates/diagnostics/src/toolbar_controls.rs index 897e2ccf40f573591d7b9e712e65928a17b28413..3c09e3fad91b952a2447cd78d30645edd3a9c44a 100644 --- a/crates/diagnostics/src/toolbar_controls.rs +++ b/crates/diagnostics/src/toolbar_controls.rs @@ -1,7 +1,7 @@ use crate::ProjectDiagnosticsEditor; use gpui::{div, EventEmitter, ParentElement, Render, ViewContext, WeakView}; use ui::prelude::*; -use ui::{Icon, IconButton, Tooltip}; +use ui::{IconButton, IconName, Tooltip}; use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; pub struct ToolbarControls { @@ -24,7 +24,7 @@ impl Render for ToolbarControls { }; div().child( - IconButton::new("toggle-warnings", Icon::ExclamationTriangle) + IconButton::new("toggle-warnings", IconName::ExclamationTriangle) .tooltip(move |cx| Tooltip::text(tooltip, cx)) .on_click(cx.listener(|this, _, cx| { if let Some(editor) = this.editor.as_ref().and_then(|editor| editor.upgrade()) { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 231f76218a44125e6c42f2a99f98027f98414ab1..9858cf8372eec5a57e4f1eeb419d06147d7ce51c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -99,8 +99,8 @@ use sum_tree::TreeMap; use text::{OffsetUtf16, Rope}; use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings}; use ui::{ - h_stack, prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, ListItem, Popover, - Tooltip, + h_stack, prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, ListItem, + Popover, Tooltip, }; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace}; @@ -4223,7 +4223,7 @@ impl Editor { ) -> Option { if self.available_code_actions.is_some() { Some( - IconButton::new("code_actions_indicator", ui::Icon::Bolt) + IconButton::new("code_actions_indicator", ui::IconName::Bolt) .icon_size(IconSize::Small) .icon_color(Color::Muted) .selected(is_active) @@ -4257,7 +4257,7 @@ impl Editor { fold_data .map(|(fold_status, buffer_row, active)| { (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { - IconButton::new(ix as usize, ui::Icon::ChevronDown) + IconButton::new(ix as usize, ui::IconName::ChevronDown) .on_click(cx.listener(move |editor, _e, cx| match fold_status { FoldStatus::Folded => { editor.unfold_at(&UnfoldAt { buffer_row }, cx); @@ -4269,7 +4269,7 @@ impl Editor { .icon_color(ui::Color::Muted) .icon_size(ui::IconSize::Small) .selected(fold_status == FoldStatus::Folded) - .selected_icon(ui::Icon::ChevronRight) + .selected_icon(ui::IconName::ChevronRight) .size(ui::ButtonSize::None) }) }) @@ -9739,7 +9739,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren ), ) .child( - IconButton::new(("copy-block", cx.block_id), Icon::Copy) + IconButton::new(("copy-block", cx.block_id), IconName::Copy) .icon_color(Color::Muted) .size(ButtonSize::Compact) .style(ButtonStyle::Transparent) diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index a02540bc5b3339e576f1a1eae336db119bd845ed..377d4cea5c11f3c3c7581e5a2d8b8034811f39e6 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -1,5 +1,5 @@ use gpui::{Render, ViewContext, WeakView}; -use ui::{prelude::*, ButtonCommon, Icon, IconButton, Tooltip}; +use ui::{prelude::*, ButtonCommon, IconButton, IconName, Tooltip}; use workspace::{item::ItemHandle, StatusItemView, Workspace}; use crate::{feedback_modal::FeedbackModal, GiveFeedback}; @@ -27,7 +27,7 @@ impl Render for DeployFeedbackButton { }) }) .is_some(); - IconButton::new("give-feedback", Icon::Envelope) + IconButton::new("give-feedback", IconName::Envelope) .style(ui::ButtonStyle::Subtle) .icon_size(IconSize::Small) .selected(is_open) diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index b197d602338e052b319623e9b1ad6a1e6f7d7b53..bf7a0715604f20ec6eae1d28ea4988016a8cd2cf 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -488,7 +488,7 @@ impl Render for FeedbackModal { .child( Button::new("community_repository", "Community Repository") .style(ButtonStyle::Transparent) - .icon(Icon::ExternalLink) + .icon(IconName::ExternalLink) .icon_position(IconPosition::End) .icon_size(IconSize::Small) .on_click(open_community_repo), diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 727ab7e859d237c8729401e6dcee90f8529616c8..251e26ebfba004b81a49c1ce28956e01f42bbce5 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -30,7 +30,7 @@ use std::{ sync::Arc, }; use theme::ThemeSettings; -use ui::{prelude::*, v_stack, ContextMenu, IconElement, KeyBinding, Label, ListItem}; +use ui::{prelude::*, v_stack, ContextMenu, Icon, KeyBinding, Label, ListItem}; use unicase::UniCase; use util::{maybe, ResultExt, TryFutureExt}; use workspace::{ @@ -1403,7 +1403,7 @@ impl ProjectPanel { .indent_step_size(px(settings.indent_size)) .selected(is_selected) .child(if let Some(icon) = &icon { - div().child(IconElement::from_path(icon.to_string()).color(Color::Muted)) + div().child(Icon::from_path(icon.to_string()).color(Color::Muted)) } else { div().size(IconSize::default().rems()).invisible() }) @@ -1590,7 +1590,7 @@ impl Render for DraggedProjectEntryView { .indent_level(self.details.depth) .indent_step_size(px(settings.indent_size)) .child(if let Some(icon) = &self.details.icon { - div().child(IconElement::from_path(icon.to_string())) + div().child(Icon::from_path(icon.to_string())) } else { div() }) @@ -1640,8 +1640,8 @@ impl Panel for ProjectPanel { cx.notify(); } - fn icon(&self, _: &WindowContext) -> Option { - Some(ui::Icon::FileTree) + fn icon(&self, _: &WindowContext) -> Option { + Some(ui::IconName::FileTree) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { diff --git a/crates/quick_action_bar/src/quick_action_bar.rs b/crates/quick_action_bar/src/quick_action_bar.rs index b40794c2fad4cfe4d02be825e1ec49be82e29ff4..cf4941bcec66cdef77f3f4453a6b3eb25d8b1321 100644 --- a/crates/quick_action_bar/src/quick_action_bar.rs +++ b/crates/quick_action_bar/src/quick_action_bar.rs @@ -6,7 +6,7 @@ use gpui::{ Subscription, View, ViewContext, WeakView, }; use search::{buffer_search, BufferSearchBar}; -use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip}; +use ui::{prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, Tooltip}; use workspace::{ item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, }; @@ -43,7 +43,7 @@ impl Render for QuickActionBar { let inlay_hints_button = Some(QuickActionBarButton::new( "toggle inlay hints", - Icon::InlayHint, + IconName::InlayHint, editor.read(cx).inlay_hints_enabled(), Box::new(editor::ToggleInlayHints), "Toggle Inlay Hints", @@ -60,7 +60,7 @@ impl Render for QuickActionBar { let search_button = Some(QuickActionBarButton::new( "toggle buffer search", - Icon::MagnifyingGlass, + IconName::MagnifyingGlass, !self.buffer_search_bar.read(cx).is_dismissed(), Box::new(buffer_search::Deploy { focus: false }), "Buffer Search", @@ -77,7 +77,7 @@ impl Render for QuickActionBar { let assistant_button = QuickActionBarButton::new( "toggle inline assistant", - Icon::MagicWand, + IconName::MagicWand, false, Box::new(InlineAssist), "Inline Assist", @@ -107,7 +107,7 @@ impl EventEmitter for QuickActionBar {} #[derive(IntoElement)] struct QuickActionBarButton { id: ElementId, - icon: Icon, + icon: IconName, toggled: bool, action: Box, tooltip: SharedString, @@ -117,7 +117,7 @@ struct QuickActionBarButton { impl QuickActionBarButton { fn new( id: impl Into, - icon: Icon, + icon: IconName, toggled: bool, action: Box, tooltip: impl Into, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index c889f0a4a4c11d3f104e130e34e5b87c092565d6..9b2719936080fd14ba65c8f0b044c11455815f2c 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -21,7 +21,7 @@ use settings::Settings; use std::{any::Any, sync::Arc}; use theme::ThemeSettings; -use ui::{h_stack, prelude::*, Icon, IconButton, IconElement, ToggleButton, Tooltip}; +use ui::{h_stack, prelude::*, Icon, IconButton, IconName, ToggleButton, Tooltip}; use util::ResultExt; use workspace::{ item::ItemHandle, @@ -225,7 +225,7 @@ impl Render for BufferSearchBar { .border_color(editor_border) .min_w(rems(384. / 16.)) .rounded_lg() - .child(IconElement::new(Icon::MagnifyingGlass)) + .child(Icon::new(IconName::MagnifyingGlass)) .child(self.render_text_input(&self.query_editor, cx)) .children(supported_options.case.then(|| { self.render_search_option_button( @@ -287,7 +287,7 @@ impl Render for BufferSearchBar { this.child( IconButton::new( "buffer-search-bar-toggle-replace-button", - Icon::Replace, + IconName::Replace, ) .style(ButtonStyle::Subtle) .when(self.replace_enabled, |button| { @@ -323,7 +323,7 @@ impl Render for BufferSearchBar { ) .when(should_show_replace_input, |this| { this.child( - IconButton::new("search-replace-next", ui::Icon::ReplaceNext) + IconButton::new("search-replace-next", ui::IconName::ReplaceNext) .tooltip(move |cx| { Tooltip::for_action("Replace next", &ReplaceNext, cx) }) @@ -332,7 +332,7 @@ impl Render for BufferSearchBar { })), ) .child( - IconButton::new("search-replace-all", ui::Icon::ReplaceAll) + IconButton::new("search-replace-all", ui::IconName::ReplaceAll) .tooltip(move |cx| { Tooltip::for_action("Replace all", &ReplaceAll, cx) }) @@ -350,7 +350,7 @@ impl Render for BufferSearchBar { .gap_0p5() .flex_none() .child( - IconButton::new("select-all", ui::Icon::SelectAll) + IconButton::new("select-all", ui::IconName::SelectAll) .on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone())) .tooltip(|cx| { Tooltip::for_action("Select all matches", &SelectAllMatches, cx) @@ -358,13 +358,13 @@ impl Render for BufferSearchBar { ) .children(match_count) .child(render_nav_button( - ui::Icon::ChevronLeft, + ui::IconName::ChevronLeft, self.active_match_index.is_some(), "Select previous match", &SelectPrevMatch, )) .child(render_nav_button( - ui::Icon::ChevronRight, + ui::IconName::ChevronRight, self.active_match_index.is_some(), "Select next match", &SelectNextMatch, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index a370cca9f69c2e89b866232ccb2c385d7726ce25..5cdf614c1b9f8e22c416866db3a863e2458251c2 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -38,7 +38,7 @@ use std::{ use theme::ThemeSettings; use ui::{ - h_stack, prelude::*, v_stack, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize, + h_stack, prelude::*, v_stack, Icon, IconButton, IconName, Label, LabelCommon, LabelSize, Selectable, ToggleButton, Tooltip, }; use util::{paths::PathMatcher, ResultExt as _}; @@ -432,7 +432,7 @@ impl Item for ProjectSearchView { .unwrap_or_else(|| "Project search".into()); h_stack() .gap_2() - .child(IconElement::new(Icon::MagnifyingGlass).color(if selected { + .child(Icon::new(IconName::MagnifyingGlass).color(if selected { Color::Default } else { Color::Muted @@ -1616,12 +1616,12 @@ impl Render for ProjectSearchBar { .on_action(cx.listener(|this, action, cx| this.confirm(action, cx))) .on_action(cx.listener(|this, action, cx| this.previous_history_query(action, cx))) .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx))) - .child(IconElement::new(Icon::MagnifyingGlass)) + .child(Icon::new(IconName::MagnifyingGlass)) .child(self.render_text_input(&search.query_editor, cx)) .child( h_stack() .child( - IconButton::new("project-search-filter-button", Icon::Filter) + IconButton::new("project-search-filter-button", IconName::Filter) .tooltip(|cx| { Tooltip::for_action("Toggle filters", &ToggleFilters, cx) }) @@ -1639,7 +1639,7 @@ impl Render for ProjectSearchBar { this.child( IconButton::new( "project-search-case-sensitive", - Icon::CaseSensitive, + IconName::CaseSensitive, ) .tooltip(|cx| { Tooltip::for_action( @@ -1659,7 +1659,7 @@ impl Render for ProjectSearchBar { )), ) .child( - IconButton::new("project-search-whole-word", Icon::WholeWord) + IconButton::new("project-search-whole-word", IconName::WholeWord) .tooltip(|cx| { Tooltip::for_action( "Toggle whole word", @@ -1738,7 +1738,7 @@ impl Render for ProjectSearchBar { }), ) .child( - IconButton::new("project-search-toggle-replace", Icon::Replace) + IconButton::new("project-search-toggle-replace", IconName::Replace) .on_click(cx.listener(|this, _, cx| { this.toggle_replace(&ToggleReplace, cx); })) @@ -1755,7 +1755,7 @@ impl Render for ProjectSearchBar { .border_1() .border_color(cx.theme().colors().border) .rounded_lg() - .child(IconElement::new(Icon::Replace).size(ui::IconSize::Small)) + .child(Icon::new(IconName::Replace).size(ui::IconSize::Small)) .child(self.render_text_input(&search.replacement_editor, cx)) } else { // Fill out the space if we don't have a replacement editor. @@ -1764,7 +1764,7 @@ impl Render for ProjectSearchBar { let actions_column = h_stack() .when(search.replace_enabled, |this| { this.child( - IconButton::new("project-search-replace-next", Icon::ReplaceNext) + IconButton::new("project-search-replace-next", IconName::ReplaceNext) .on_click(cx.listener(|this, _, cx| { if let Some(search) = this.active_project_search.as_ref() { search.update(cx, |this, cx| { @@ -1775,7 +1775,7 @@ impl Render for ProjectSearchBar { .tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)), ) .child( - IconButton::new("project-search-replace-all", Icon::ReplaceAll) + IconButton::new("project-search-replace-all", IconName::ReplaceAll) .on_click(cx.listener(|this, _, cx| { if let Some(search) = this.active_project_search.as_ref() { search.update(cx, |this, cx| { @@ -1796,7 +1796,7 @@ impl Render for ProjectSearchBar { this }) .child( - IconButton::new("project-search-prev-match", Icon::ChevronLeft) + IconButton::new("project-search-prev-match", IconName::ChevronLeft) .disabled(search.active_match_index.is_none()) .on_click(cx.listener(|this, _, cx| { if let Some(search) = this.active_project_search.as_ref() { @@ -1810,7 +1810,7 @@ impl Render for ProjectSearchBar { }), ) .child( - IconButton::new("project-search-next-match", Icon::ChevronRight) + IconButton::new("project-search-next-match", IconName::ChevronRight) .disabled(search.active_match_index.is_none()) .on_click(cx.listener(|this, _, cx| { if let Some(search) = this.active_project_search.as_ref() { diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index f0301a5bcc7a637c17d47c59f5102f352ac3840c..1b29801e03a8b370411be0d1def77c392cccb1dd 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -60,11 +60,11 @@ impl SearchOptions { } } - pub fn icon(&self) -> ui::Icon { + pub fn icon(&self) -> ui::IconName { match *self { - SearchOptions::WHOLE_WORD => ui::Icon::WholeWord, - SearchOptions::CASE_SENSITIVE => ui::Icon::CaseSensitive, - SearchOptions::INCLUDE_IGNORED => ui::Icon::FileGit, + SearchOptions::WHOLE_WORD => ui::IconName::WholeWord, + SearchOptions::CASE_SENSITIVE => ui::IconName::CaseSensitive, + SearchOptions::INCLUDE_IGNORED => ui::IconName::FileGit, _ => panic!("{:?} is not a named SearchOption", self), } } diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 628be3112ecc06e9d00e8d33361e8f17fa6efd54..0594036c25483c8dadcbfdf5988edbc1ba12a65f 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -3,7 +3,7 @@ use ui::IconButton; use ui::{prelude::*, Tooltip}; pub(super) fn render_nav_button( - icon: ui::Icon, + icon: ui::IconName, active: bool, tooltip: &'static str, action: &'static dyn Action, diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 99929535700e2badbe0c447ac6dfc4ee5998e7e2..d0b52f5eb217ed60cc9b62e20657e5033506f133 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -19,7 +19,7 @@ use workspace::{ dock::{DockPosition, Panel, PanelEvent}, item::Item, pane, - ui::Icon, + ui::IconName, DraggedTab, Pane, Workspace, }; @@ -71,7 +71,7 @@ impl TerminalPanel { h_stack() .gap_2() .child( - IconButton::new("plus", Icon::Plus) + IconButton::new("plus", IconName::Plus) .icon_size(IconSize::Small) .on_click(move |_, cx| { terminal_panel @@ -82,10 +82,10 @@ impl TerminalPanel { ) .child({ let zoomed = pane.is_zoomed(); - IconButton::new("toggle_zoom", Icon::Maximize) + IconButton::new("toggle_zoom", IconName::Maximize) .icon_size(IconSize::Small) .selected(zoomed) - .selected_icon(Icon::Minimize) + .selected_icon(IconName::Minimize) .on_click(cx.listener(|pane, _, cx| { pane.toggle_zoom(&workspace::ToggleZoom, cx); })) @@ -477,8 +477,8 @@ impl Panel for TerminalPanel { "TerminalPanel" } - fn icon(&self, _cx: &WindowContext) -> Option { - Some(Icon::Terminal) + fn icon(&self, _cx: &WindowContext) -> Option { + Some(IconName::Terminal) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index f4fb6105cb8bcf476a12d68d29aadb0d942a22dc..4d2e78f0daddacb0bc33673b8a14c29f54d3fdb4 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -20,7 +20,7 @@ use terminal::{ Clear, Copy, Event, MaybeNavigationTarget, Paste, ShowCharacterPalette, Terminal, }; use terminal_element::TerminalElement; -use ui::{h_stack, prelude::*, ContextMenu, Icon, IconElement, Label}; +use ui::{h_stack, prelude::*, ContextMenu, Icon, IconName, Label}; use util::{paths::PathLikeWithPosition, ResultExt}; use workspace::{ item::{BreadcrumbText, Item, ItemEvent}, @@ -690,7 +690,7 @@ impl Item for TerminalView { let title = self.terminal().read(cx).title(true); h_stack() .gap_2() - .child(IconElement::new(Icon::Terminal)) + .child(Icon::new(IconName::Terminal)) .child(Label::new(title).color(if selected { Color::Default } else { diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index 1e60aae03b11e84eda1a2041565c6b75a5fae79f..398f8f10e27831a07bdca21f837e1425f396c5e9 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -2,7 +2,7 @@ use gpui::{AnyView, DefiniteLength}; use crate::{prelude::*, IconPosition, KeyBinding}; use crate::{ - ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle, + ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle, }; use super::button_icon::ButtonIcon; @@ -14,11 +14,11 @@ pub struct Button { label_color: Option, label_size: Option, selected_label: Option, - icon: Option, + icon: Option, icon_position: Option, icon_size: Option, icon_color: Option, - selected_icon: Option, + selected_icon: Option, key_binding: Option, } @@ -54,7 +54,7 @@ impl Button { self } - pub fn icon(mut self, icon: impl Into>) -> Self { + pub fn icon(mut self, icon: impl Into>) -> Self { self.icon = icon.into(); self } @@ -74,7 +74,7 @@ impl Button { self } - pub fn selected_icon(mut self, icon: impl Into>) -> Self { + pub fn selected_icon(mut self, icon: impl Into>) -> Self { self.selected_icon = icon.into(); self } diff --git a/crates/ui/src/components/button/button_icon.rs b/crates/ui/src/components/button/button_icon.rs index 15538bb24d79b9f17c3fcef8c4de37466e84fc66..b8f5427d30aaa6dfb21b802bdd49922de9e17433 100644 --- a/crates/ui/src/components/button/button_icon.rs +++ b/crates/ui/src/components/button/button_icon.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, Icon, IconElement, IconSize}; +use crate::{prelude::*, Icon, IconName, IconSize}; /// An icon that appears within a button. /// @@ -6,17 +6,17 @@ use crate::{prelude::*, Icon, IconElement, IconSize}; /// or as a standalone icon, like in [`IconButton`](crate::IconButton). #[derive(IntoElement)] pub(super) struct ButtonIcon { - icon: Icon, + icon: IconName, size: IconSize, color: Color, disabled: bool, selected: bool, - selected_icon: Option, + selected_icon: Option, selected_style: Option, } impl ButtonIcon { - pub fn new(icon: Icon) -> Self { + pub fn new(icon: IconName) -> Self { Self { icon, size: IconSize::default(), @@ -44,7 +44,7 @@ impl ButtonIcon { self } - pub fn selected_icon(mut self, icon: impl Into>) -> Self { + pub fn selected_icon(mut self, icon: impl Into>) -> Self { self.selected_icon = icon.into(); self } @@ -88,6 +88,6 @@ impl RenderOnce for ButtonIcon { self.color }; - IconElement::new(icon).size(self.size).color(icon_color) + Icon::new(icon).size(self.size).color(icon_color) } } diff --git a/crates/ui/src/components/button/icon_button.rs b/crates/ui/src/components/button/icon_button.rs index d9ed6ccb5d86a2c5122ef6cd2fd364be235566bc..7c5313497c77ff6532838966001d2cbd4a3688e0 100644 --- a/crates/ui/src/components/button/icon_button.rs +++ b/crates/ui/src/components/button/icon_button.rs @@ -1,21 +1,21 @@ use gpui::{AnyView, DefiniteLength}; use crate::{prelude::*, SelectableButton}; -use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize}; +use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize}; use super::button_icon::ButtonIcon; #[derive(IntoElement)] pub struct IconButton { base: ButtonLike, - icon: Icon, + icon: IconName, icon_size: IconSize, icon_color: Color, - selected_icon: Option, + selected_icon: Option, } impl IconButton { - pub fn new(id: impl Into, icon: Icon) -> Self { + pub fn new(id: impl Into, icon: IconName) -> Self { Self { base: ButtonLike::new(id), icon, @@ -35,7 +35,7 @@ impl IconButton { self } - pub fn selected_icon(mut self, icon: impl Into>) -> Self { + pub fn selected_icon(mut self, icon: impl Into>) -> Self { self.selected_icon = icon.into(); self } diff --git a/crates/ui/src/components/checkbox.rs b/crates/ui/src/components/checkbox.rs index 3b778420291980ff70e3e8cebaa5b3c4d411033f..08c95f2d939f0fc76be90d2186fab9a6788c8f8f 100644 --- a/crates/ui/src/components/checkbox.rs +++ b/crates/ui/src/components/checkbox.rs @@ -1,7 +1,7 @@ use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext}; use crate::prelude::*; -use crate::{Color, Icon, IconElement, Selection}; +use crate::{Color, Icon, IconName, Selection}; pub type CheckHandler = Box; @@ -47,7 +47,7 @@ impl RenderOnce for Checkbox { let group_id = format!("checkbox_group_{:?}", self.id); let icon = match self.checked { - Selection::Selected => Some(IconElement::new(Icon::Check).size(IconSize::Small).color( + Selection::Selected => Some(Icon::new(IconName::Check).size(IconSize::Small).color( if self.disabled { Color::Disabled } else { @@ -55,7 +55,7 @@ impl RenderOnce for Checkbox { }, )), Selection::Indeterminate => Some( - IconElement::new(Icon::Dash) + Icon::new(IconName::Dash) .size(IconSize::Small) .color(if self.disabled { Color::Disabled diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 8666ec65651d6f00cbe225f7b065521673326037..098c54f33cb98d831c0806a2b4ec9f5b0a18ad07 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -1,6 +1,6 @@ use crate::{ - h_stack, prelude::*, v_stack, Icon, IconElement, KeyBinding, Label, List, ListItem, - ListSeparator, ListSubHeader, + h_stack, prelude::*, v_stack, Icon, IconName, KeyBinding, Label, List, ListItem, ListSeparator, + ListSubHeader, }; use gpui::{ px, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, @@ -14,7 +14,7 @@ enum ContextMenuItem { Header(SharedString), Entry { label: SharedString, - icon: Option, + icon: Option, handler: Rc, action: Option>, }, @@ -117,7 +117,7 @@ impl ContextMenu { label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())), - icon: Some(Icon::Link), + icon: Some(IconName::Link), }); self } @@ -280,7 +280,7 @@ impl Render for ContextMenu { h_stack() .gap_1() .child(Label::new(label.clone())) - .child(IconElement::new(*icon)) + .child(Icon::new(*icon)) .into_any_element() } else { Label::new(label.clone()).into_any_element() diff --git a/crates/ui/src/components/disclosure.rs b/crates/ui/src/components/disclosure.rs index d4349f61a0f53b7f70af3255576aaa4c715796ae..59651ddb0b5ec9154c0180ca89e2331007cd3404 100644 --- a/crates/ui/src/components/disclosure.rs +++ b/crates/ui/src/components/disclosure.rs @@ -1,6 +1,6 @@ use gpui::ClickEvent; -use crate::{prelude::*, Color, Icon, IconButton, IconSize}; +use crate::{prelude::*, Color, IconButton, IconName, IconSize}; #[derive(IntoElement)] pub struct Disclosure { @@ -32,8 +32,8 @@ impl RenderOnce for Disclosure { IconButton::new( self.id, match self.is_open { - true => Icon::ChevronDown, - false => Icon::ChevronRight, + true => IconName::ChevronDown, + false => IconName::ChevronRight, }, ) .icon_color(Color::Muted) diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 4c6e48c0fc034dbcfbd4454c920b2d4a994b92b0..908e76ef918b56aefff6949e86ea6473a272253d 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -22,7 +22,7 @@ impl IconSize { } #[derive(Debug, PartialEq, Copy, Clone, EnumIter)] -pub enum Icon { +pub enum IconName { Ai, ArrowDown, ArrowLeft, @@ -111,118 +111,108 @@ pub enum Icon { ZedXCopilot, } -impl Icon { +impl IconName { pub fn path(self) -> &'static str { match self { - Icon::Ai => "icons/ai.svg", - Icon::ArrowDown => "icons/arrow_down.svg", - Icon::ArrowLeft => "icons/arrow_left.svg", - Icon::ArrowRight => "icons/arrow_right.svg", - Icon::ArrowUp => "icons/arrow_up.svg", - Icon::ArrowUpRight => "icons/arrow_up_right.svg", - Icon::ArrowCircle => "icons/arrow_circle.svg", - Icon::AtSign => "icons/at_sign.svg", - Icon::AudioOff => "icons/speaker_off.svg", - Icon::AudioOn => "icons/speaker_loud.svg", - Icon::Backspace => "icons/backspace.svg", - Icon::Bell => "icons/bell.svg", - Icon::BellOff => "icons/bell_off.svg", - Icon::BellRing => "icons/bell_ring.svg", - Icon::Bolt => "icons/bolt.svg", - Icon::CaseSensitive => "icons/case_insensitive.svg", - Icon::Check => "icons/check.svg", - Icon::ChevronDown => "icons/chevron_down.svg", - Icon::ChevronLeft => "icons/chevron_left.svg", - Icon::ChevronRight => "icons/chevron_right.svg", - Icon::ChevronUp => "icons/chevron_up.svg", - Icon::Close => "icons/x.svg", - Icon::Collab => "icons/user_group_16.svg", - Icon::Command => "icons/command.svg", - Icon::Control => "icons/control.svg", - Icon::Copilot => "icons/copilot.svg", - Icon::CopilotDisabled => "icons/copilot_disabled.svg", - Icon::CopilotError => "icons/copilot_error.svg", - Icon::CopilotInit => "icons/copilot_init.svg", - Icon::Copy => "icons/copy.svg", - Icon::Dash => "icons/dash.svg", - Icon::Delete => "icons/delete.svg", - Icon::Disconnected => "icons/disconnected.svg", - Icon::Ellipsis => "icons/ellipsis.svg", - Icon::Envelope => "icons/feedback.svg", - Icon::Escape => "icons/escape.svg", - Icon::ExclamationTriangle => "icons/warning.svg", - Icon::Exit => "icons/exit.svg", - Icon::ExternalLink => "icons/external_link.svg", - Icon::File => "icons/file.svg", - Icon::FileDoc => "icons/file_icons/book.svg", - Icon::FileGeneric => "icons/file_icons/file.svg", - Icon::FileGit => "icons/file_icons/git.svg", - Icon::FileLock => "icons/file_icons/lock.svg", - Icon::FileRust => "icons/file_icons/rust.svg", - Icon::FileToml => "icons/file_icons/toml.svg", - Icon::FileTree => "icons/project.svg", - Icon::Filter => "icons/filter.svg", - Icon::Folder => "icons/file_icons/folder.svg", - Icon::FolderOpen => "icons/file_icons/folder_open.svg", - Icon::FolderX => "icons/stop_sharing.svg", - Icon::Github => "icons/github.svg", - Icon::Hash => "icons/hash.svg", - Icon::InlayHint => "icons/inlay_hint.svg", - Icon::Link => "icons/link.svg", - Icon::MagicWand => "icons/magic_wand.svg", - Icon::MagnifyingGlass => "icons/magnifying_glass.svg", - Icon::MailOpen => "icons/mail_open.svg", - Icon::Maximize => "icons/maximize.svg", - Icon::Menu => "icons/menu.svg", - Icon::MessageBubbles => "icons/conversations.svg", - Icon::Mic => "icons/mic.svg", - Icon::MicMute => "icons/mic_mute.svg", - Icon::Minimize => "icons/minimize.svg", - Icon::Option => "icons/option.svg", - Icon::PageDown => "icons/page_down.svg", - Icon::PageUp => "icons/page_up.svg", - Icon::Plus => "icons/plus.svg", - Icon::Public => "icons/public.svg", - Icon::Quote => "icons/quote.svg", - Icon::Replace => "icons/replace.svg", - Icon::ReplaceAll => "icons/replace_all.svg", - Icon::ReplaceNext => "icons/replace_next.svg", - Icon::Return => "icons/return.svg", - Icon::Screen => "icons/desktop.svg", - Icon::SelectAll => "icons/select_all.svg", - Icon::Shift => "icons/shift.svg", - Icon::Snip => "icons/snip.svg", - Icon::Space => "icons/space.svg", - Icon::Split => "icons/split.svg", - Icon::Tab => "icons/tab.svg", - Icon::Terminal => "icons/terminal.svg", - Icon::Update => "icons/update.svg", - Icon::WholeWord => "icons/word_search.svg", - Icon::XCircle => "icons/error.svg", - Icon::ZedXCopilot => "icons/zed_x_copilot.svg", + IconName::Ai => "icons/ai.svg", + IconName::ArrowDown => "icons/arrow_down.svg", + IconName::ArrowLeft => "icons/arrow_left.svg", + IconName::ArrowRight => "icons/arrow_right.svg", + IconName::ArrowUp => "icons/arrow_up.svg", + IconName::ArrowUpRight => "icons/arrow_up_right.svg", + IconName::ArrowCircle => "icons/arrow_circle.svg", + IconName::AtSign => "icons/at_sign.svg", + IconName::AudioOff => "icons/speaker_off.svg", + IconName::AudioOn => "icons/speaker_loud.svg", + IconName::Backspace => "icons/backspace.svg", + IconName::Bell => "icons/bell.svg", + IconName::BellOff => "icons/bell_off.svg", + IconName::BellRing => "icons/bell_ring.svg", + IconName::Bolt => "icons/bolt.svg", + IconName::CaseSensitive => "icons/case_insensitive.svg", + IconName::Check => "icons/check.svg", + IconName::ChevronDown => "icons/chevron_down.svg", + IconName::ChevronLeft => "icons/chevron_left.svg", + IconName::ChevronRight => "icons/chevron_right.svg", + IconName::ChevronUp => "icons/chevron_up.svg", + IconName::Close => "icons/x.svg", + IconName::Collab => "icons/user_group_16.svg", + IconName::Command => "icons/command.svg", + IconName::Control => "icons/control.svg", + IconName::Copilot => "icons/copilot.svg", + IconName::CopilotDisabled => "icons/copilot_disabled.svg", + IconName::CopilotError => "icons/copilot_error.svg", + IconName::CopilotInit => "icons/copilot_init.svg", + IconName::Copy => "icons/copy.svg", + IconName::Dash => "icons/dash.svg", + IconName::Delete => "icons/delete.svg", + IconName::Disconnected => "icons/disconnected.svg", + IconName::Ellipsis => "icons/ellipsis.svg", + IconName::Envelope => "icons/feedback.svg", + IconName::Escape => "icons/escape.svg", + IconName::ExclamationTriangle => "icons/warning.svg", + IconName::Exit => "icons/exit.svg", + IconName::ExternalLink => "icons/external_link.svg", + IconName::File => "icons/file.svg", + IconName::FileDoc => "icons/file_icons/book.svg", + IconName::FileGeneric => "icons/file_icons/file.svg", + IconName::FileGit => "icons/file_icons/git.svg", + IconName::FileLock => "icons/file_icons/lock.svg", + IconName::FileRust => "icons/file_icons/rust.svg", + IconName::FileToml => "icons/file_icons/toml.svg", + IconName::FileTree => "icons/project.svg", + IconName::Filter => "icons/filter.svg", + IconName::Folder => "icons/file_icons/folder.svg", + IconName::FolderOpen => "icons/file_icons/folder_open.svg", + IconName::FolderX => "icons/stop_sharing.svg", + IconName::Github => "icons/github.svg", + IconName::Hash => "icons/hash.svg", + IconName::InlayHint => "icons/inlay_hint.svg", + IconName::Link => "icons/link.svg", + IconName::MagicWand => "icons/magic_wand.svg", + IconName::MagnifyingGlass => "icons/magnifying_glass.svg", + IconName::MailOpen => "icons/mail_open.svg", + IconName::Maximize => "icons/maximize.svg", + IconName::Menu => "icons/menu.svg", + IconName::MessageBubbles => "icons/conversations.svg", + IconName::Mic => "icons/mic.svg", + IconName::MicMute => "icons/mic_mute.svg", + IconName::Minimize => "icons/minimize.svg", + IconName::Option => "icons/option.svg", + IconName::PageDown => "icons/page_down.svg", + IconName::PageUp => "icons/page_up.svg", + IconName::Plus => "icons/plus.svg", + IconName::Public => "icons/public.svg", + IconName::Quote => "icons/quote.svg", + IconName::Replace => "icons/replace.svg", + IconName::ReplaceAll => "icons/replace_all.svg", + IconName::ReplaceNext => "icons/replace_next.svg", + IconName::Return => "icons/return.svg", + IconName::Screen => "icons/desktop.svg", + IconName::SelectAll => "icons/select_all.svg", + IconName::Shift => "icons/shift.svg", + IconName::Snip => "icons/snip.svg", + IconName::Space => "icons/space.svg", + IconName::Split => "icons/split.svg", + IconName::Tab => "icons/tab.svg", + IconName::Terminal => "icons/terminal.svg", + IconName::Update => "icons/update.svg", + IconName::WholeWord => "icons/word_search.svg", + IconName::XCircle => "icons/error.svg", + IconName::ZedXCopilot => "icons/zed_x_copilot.svg", } } } #[derive(IntoElement)] -pub struct IconElement { +pub struct Icon { path: SharedString, color: Color, size: IconSize, } -impl RenderOnce for IconElement { - fn render(self, cx: &mut WindowContext) -> impl IntoElement { - svg() - .size(self.size.rems()) - .flex_none() - .path(self.path) - .text_color(self.color.color(cx)) - } -} - -impl IconElement { - pub fn new(icon: Icon) -> Self { +impl Icon { + pub fn new(icon: IconName) -> Self { Self { path: icon.path().into(), color: Color::default(), @@ -248,3 +238,13 @@ impl IconElement { self } } + +impl RenderOnce for Icon { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + svg() + .size(self.size.rems()) + .flex_none() + .path(self.path) + .text_color(self.color.color(cx)) + } +} diff --git a/crates/ui/src/components/keybinding.rs b/crates/ui/src/components/keybinding.rs index 671f9810831649f8f83dac2e25f88f2223febc40..e0e0583b7cb25e4966c183ae54d9f4742c66935d 100644 --- a/crates/ui/src/components/keybinding.rs +++ b/crates/ui/src/components/keybinding.rs @@ -1,4 +1,4 @@ -use crate::{h_stack, prelude::*, Icon, IconElement, IconSize}; +use crate::{h_stack, prelude::*, Icon, IconName, IconSize}; use gpui::{relative, rems, Action, FocusHandle, IntoElement, Keystroke}; #[derive(IntoElement, Clone)] @@ -26,16 +26,16 @@ impl RenderOnce for KeyBinding { .text_color(cx.theme().colors().text_muted) .when(keystroke.modifiers.function, |el| el.child(Key::new("fn"))) .when(keystroke.modifiers.control, |el| { - el.child(KeyIcon::new(Icon::Control)) + el.child(KeyIcon::new(IconName::Control)) }) .when(keystroke.modifiers.alt, |el| { - el.child(KeyIcon::new(Icon::Option)) + el.child(KeyIcon::new(IconName::Option)) }) .when(keystroke.modifiers.command, |el| { - el.child(KeyIcon::new(Icon::Command)) + el.child(KeyIcon::new(IconName::Command)) }) .when(keystroke.modifiers.shift, |el| { - el.child(KeyIcon::new(Icon::Shift)) + el.child(KeyIcon::new(IconName::Shift)) }) .when_some(key_icon, |el, icon| el.child(KeyIcon::new(icon))) .when(key_icon.is_none(), |el| { @@ -62,21 +62,21 @@ impl KeyBinding { Some(Self::new(key_binding)) } - fn icon_for_key(keystroke: &Keystroke) -> Option { + fn icon_for_key(keystroke: &Keystroke) -> Option { match keystroke.key.as_str() { - "left" => Some(Icon::ArrowLeft), - "right" => Some(Icon::ArrowRight), - "up" => Some(Icon::ArrowUp), - "down" => Some(Icon::ArrowDown), - "backspace" => Some(Icon::Backspace), - "delete" => Some(Icon::Delete), - "return" => Some(Icon::Return), - "enter" => Some(Icon::Return), - "tab" => Some(Icon::Tab), - "space" => Some(Icon::Space), - "escape" => Some(Icon::Escape), - "pagedown" => Some(Icon::PageDown), - "pageup" => Some(Icon::PageUp), + "left" => Some(IconName::ArrowLeft), + "right" => Some(IconName::ArrowRight), + "up" => Some(IconName::ArrowUp), + "down" => Some(IconName::ArrowDown), + "backspace" => Some(IconName::Backspace), + "delete" => Some(IconName::Delete), + "return" => Some(IconName::Return), + "enter" => Some(IconName::Return), + "tab" => Some(IconName::Tab), + "space" => Some(IconName::Space), + "escape" => Some(IconName::Escape), + "pagedown" => Some(IconName::PageDown), + "pageup" => Some(IconName::PageUp), _ => None, } } @@ -120,13 +120,13 @@ impl Key { #[derive(IntoElement)] pub struct KeyIcon { - icon: Icon, + icon: IconName, } impl RenderOnce for KeyIcon { fn render(self, _cx: &mut WindowContext) -> impl IntoElement { div().w(rems(14. / 16.)).child( - IconElement::new(self.icon) + Icon::new(self.icon) .size(IconSize::Small) .color(Color::Muted), ) @@ -134,7 +134,7 @@ impl RenderOnce for KeyIcon { } impl KeyIcon { - pub fn new(icon: Icon) -> Self { + pub fn new(icon: IconName) -> Self { Self { icon } } } diff --git a/crates/ui/src/components/list/list_sub_header.rs b/crates/ui/src/components/list/list_sub_header.rs index 2e976b35178a2132cf1f0a6f2c154f03de249389..fc9f35e175c0d42a1517fc1227a4befc7dfdb2da 100644 --- a/crates/ui/src/components/list/list_sub_header.rs +++ b/crates/ui/src/components/list/list_sub_header.rs @@ -1,10 +1,10 @@ use crate::prelude::*; -use crate::{h_stack, Icon, IconElement, IconSize, Label}; +use crate::{h_stack, Icon, IconName, IconSize, Label}; #[derive(IntoElement)] pub struct ListSubHeader { label: SharedString, - start_slot: Option, + start_slot: Option, inset: bool, } @@ -17,7 +17,7 @@ impl ListSubHeader { } } - pub fn left_icon(mut self, left_icon: Option) -> Self { + pub fn left_icon(mut self, left_icon: Option) -> Self { self.start_slot = left_icon; self } @@ -40,11 +40,10 @@ impl RenderOnce for ListSubHeader { .flex() .gap_1() .items_center() - .children(self.start_slot.map(|i| { - IconElement::new(i) - .color(Color::Muted) - .size(IconSize::Small) - })) + .children( + self.start_slot + .map(|i| Icon::new(i).color(Color::Muted).size(IconSize::Small)), + ) .child(Label::new(self.label.clone()).color(Color::Muted)), ), ) diff --git a/crates/ui/src/components/stories/button.rs b/crates/ui/src/components/stories/button.rs index 7240812fa5a3a0d964a811795f925439def97670..c3fcdc5ae913974dcd86519715ae18ba016084fa 100644 --- a/crates/ui/src/components/stories/button.rs +++ b/crates/ui/src/components/stories/button.rs @@ -1,7 +1,7 @@ use gpui::Render; use story::Story; -use crate::{prelude::*, Icon}; +use crate::{prelude::*, IconName}; use crate::{Button, ButtonStyle}; pub struct ButtonStory; @@ -23,12 +23,12 @@ impl Render for ButtonStory { .child(Story::label("With `label_color`")) .child(Button::new("filled_with_label_color", "Click me").color(Color::Created)) .child(Story::label("With `icon`")) - .child(Button::new("filled_with_icon", "Click me").icon(Icon::FileGit)) + .child(Button::new("filled_with_icon", "Click me").icon(IconName::FileGit)) .child(Story::label("Selected with `icon`")) .child( Button::new("filled_and_selected_with_icon", "Click me") .selected(true) - .icon(Icon::FileGit), + .icon(IconName::FileGit), ) .child(Story::label("Default (Subtle)")) .child(Button::new("default_subtle", "Click me").style(ButtonStyle::Subtle)) diff --git a/crates/ui/src/components/stories/icon.rs b/crates/ui/src/components/stories/icon.rs index 83fc5980dd731bfbaa61770d58c446c47624f888..f6e750de2add47059ecc630bbc85780cfa273872 100644 --- a/crates/ui/src/components/stories/icon.rs +++ b/crates/ui/src/components/stories/icon.rs @@ -3,17 +3,17 @@ use story::Story; use strum::IntoEnumIterator; use crate::prelude::*; -use crate::{Icon, IconElement}; +use crate::{Icon, IconName}; pub struct IconStory; impl Render for IconStory { fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { - let icons = Icon::iter(); + let icons = IconName::iter(); Story::container() - .child(Story::title_for::()) + .child(Story::title_for::()) .child(Story::label("All Icons")) - .child(div().flex().gap_3().children(icons.map(IconElement::new))) + .child(div().flex().gap_3().children(icons.map(Icon::new))) } } diff --git a/crates/ui/src/components/stories/icon_button.rs b/crates/ui/src/components/stories/icon_button.rs index 66fc4affb3b0b63daa8dabe4461b9ec806c9416c..6a67183e97c73b3795fc14a71c11a16b6012f549 100644 --- a/crates/ui/src/components/stories/icon_button.rs +++ b/crates/ui/src/components/stories/icon_button.rs @@ -2,7 +2,7 @@ use gpui::Render; use story::{StoryContainer, StoryItem, StorySection}; use crate::{prelude::*, Tooltip}; -use crate::{Icon, IconButton}; +use crate::{IconButton, IconName}; pub struct IconButtonStory; @@ -10,7 +10,7 @@ impl Render for IconButtonStory { fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { let default_button = StoryItem::new( "Default", - IconButton::new("default_icon_button", Icon::Hash), + IconButton::new("default_icon_button", IconName::Hash), ) .description("Displays an icon button.") .usage( @@ -21,7 +21,7 @@ impl Render for IconButtonStory { let selected_button = StoryItem::new( "Selected", - IconButton::new("selected_icon_button", Icon::Hash).selected(true), + IconButton::new("selected_icon_button", IconName::Hash).selected(true), ) .description("Displays an icon button that is selected.") .usage( @@ -32,9 +32,9 @@ impl Render for IconButtonStory { let selected_with_selected_icon = StoryItem::new( "Selected with `selected_icon`", - IconButton::new("selected_with_selected_icon_button", Icon::AudioOn) + IconButton::new("selected_with_selected_icon_button", IconName::AudioOn) .selected(true) - .selected_icon(Icon::AudioOff), + .selected_icon(IconName::AudioOff), ) .description( "Displays an icon button that is selected and shows a different icon when selected.", @@ -49,7 +49,7 @@ impl Render for IconButtonStory { let disabled_button = StoryItem::new( "Disabled", - IconButton::new("disabled_icon_button", Icon::Hash).disabled(true), + IconButton::new("disabled_icon_button", IconName::Hash).disabled(true), ) .description("Displays an icon button that is disabled.") .usage( @@ -60,7 +60,7 @@ impl Render for IconButtonStory { let with_on_click_button = StoryItem::new( "With `on_click`", - IconButton::new("with_on_click_button", Icon::Ai).on_click(|_event, _cx| { + IconButton::new("with_on_click_button", IconName::Ai).on_click(|_event, _cx| { println!("Clicked!"); }), ) @@ -75,7 +75,7 @@ impl Render for IconButtonStory { let with_tooltip_button = StoryItem::new( "With `tooltip`", - IconButton::new("with_tooltip_button", Icon::MessageBubbles) + IconButton::new("with_tooltip_button", IconName::MessageBubbles) .tooltip(|cx| Tooltip::text("Open messages", cx)), ) .description("Displays an icon button that has a tooltip when hovered.") @@ -88,7 +88,7 @@ impl Render for IconButtonStory { let selected_with_tooltip_button = StoryItem::new( "Selected with `tooltip`", - IconButton::new("selected_with_tooltip_button", Icon::InlayHint) + IconButton::new("selected_with_tooltip_button", IconName::InlayHint) .selected(true) .tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)), ) diff --git a/crates/ui/src/components/stories/list_header.rs b/crates/ui/src/components/stories/list_header.rs index ffbf7157f5b17271633ad72b4ee54023272a831a..358dc26a875c6735373bd22c50edc904ea635597 100644 --- a/crates/ui/src/components/stories/list_header.rs +++ b/crates/ui/src/components/stories/list_header.rs @@ -2,7 +2,7 @@ use gpui::Render; use story::Story; use crate::{prelude::*, IconButton}; -use crate::{Icon, ListHeader}; +use crate::{IconName, ListHeader}; pub struct ListHeaderStory; @@ -13,19 +13,19 @@ impl Render for ListHeaderStory { .child(Story::label("Default")) .child(ListHeader::new("Section 1")) .child(Story::label("With left icon")) - .child(ListHeader::new("Section 2").start_slot(IconElement::new(Icon::Bell))) + .child(ListHeader::new("Section 2").start_slot(Icon::new(IconName::Bell))) .child(Story::label("With left icon and meta")) .child( ListHeader::new("Section 3") - .start_slot(IconElement::new(Icon::BellOff)) - .end_slot(IconButton::new("action_1", Icon::Bolt)), + .start_slot(Icon::new(IconName::BellOff)) + .end_slot(IconButton::new("action_1", IconName::Bolt)), ) .child(Story::label("With multiple meta")) .child( ListHeader::new("Section 4") - .end_slot(IconButton::new("action_1", Icon::Bolt)) - .end_slot(IconButton::new("action_2", Icon::ExclamationTriangle)) - .end_slot(IconButton::new("action_3", Icon::Plus)), + .end_slot(IconButton::new("action_1", IconName::Bolt)) + .end_slot(IconButton::new("action_2", IconName::ExclamationTriangle)) + .end_slot(IconButton::new("action_3", IconName::Plus)), ) } } diff --git a/crates/ui/src/components/stories/list_item.rs b/crates/ui/src/components/stories/list_item.rs index b3ff096d9dfe9d435e68dc7e818b661603267ca2..487f14279178cbaf3d6d4baaab5e5c2f2609eb7a 100644 --- a/crates/ui/src/components/stories/list_item.rs +++ b/crates/ui/src/components/stories/list_item.rs @@ -2,7 +2,7 @@ use gpui::Render; use story::Story; use crate::{prelude::*, Avatar}; -use crate::{Icon, ListItem}; +use crate::{IconName, ListItem}; pub struct ListItemStory; @@ -18,13 +18,13 @@ impl Render for ListItemStory { ListItem::new("inset_list_item") .inset(true) .start_slot( - IconElement::new(Icon::Bell) + Icon::new(IconName::Bell) .size(IconSize::Small) .color(Color::Muted), ) .child("Hello, world!") .end_slot( - IconElement::new(Icon::Bell) + Icon::new(IconName::Bell) .size(IconSize::Small) .color(Color::Muted), ), @@ -34,7 +34,7 @@ impl Render for ListItemStory { ListItem::new("with start slot_icon") .child("Hello, world!") .start_slot( - IconElement::new(Icon::Bell) + Icon::new(IconName::Bell) .size(IconSize::Small) .color(Color::Muted), ), diff --git a/crates/ui/src/components/stories/tab.rs b/crates/ui/src/components/stories/tab.rs index 4c63e593aaa9eab582351b62b618c7e6cae3978c..bd7b602620938775b32be3d46a41b519f2639f63 100644 --- a/crates/ui/src/components/stories/tab.rs +++ b/crates/ui/src/components/stories/tab.rs @@ -27,7 +27,7 @@ impl Render for TabStory { h_stack().child( Tab::new("tab_1") .end_slot( - IconButton::new("close_button", Icon::Close) + IconButton::new("close_button", IconName::Close) .icon_color(Color::Muted) .size(ButtonSize::None) .icon_size(IconSize::XSmall), diff --git a/crates/ui/src/components/stories/tab_bar.rs b/crates/ui/src/components/stories/tab_bar.rs index 805725315c672a34feac758f52991d0cb3d03f01..289ceff9a6f576739daacd02b57e260b295a7ae8 100644 --- a/crates/ui/src/components/stories/tab_bar.rs +++ b/crates/ui/src/components/stories/tab_bar.rs @@ -38,16 +38,19 @@ impl Render for TabBarStory { h_stack().child( TabBar::new("tab_bar_1") .start_child( - IconButton::new("navigate_backward", Icon::ArrowLeft) + IconButton::new("navigate_backward", IconName::ArrowLeft) .icon_size(IconSize::Small), ) .start_child( - IconButton::new("navigate_forward", Icon::ArrowRight) + IconButton::new("navigate_forward", IconName::ArrowRight) .icon_size(IconSize::Small), ) - .end_child(IconButton::new("new", Icon::Plus).icon_size(IconSize::Small)) .end_child( - IconButton::new("split_pane", Icon::Split).icon_size(IconSize::Small), + IconButton::new("new", IconName::Plus).icon_size(IconSize::Small), + ) + .end_child( + IconButton::new("split_pane", IconName::Split) + .icon_size(IconSize::Small), ) .children(tabs), ), diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs index 48536f59b312b5389e9892b6593919ed0adc7bd0..0a86b99f9f232927d9e92615adcf987997df7919 100644 --- a/crates/ui/src/prelude.rs +++ b/crates/ui/src/prelude.rs @@ -15,6 +15,6 @@ pub use crate::{h_stack, v_stack}; pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton}; pub use crate::{ButtonCommon, Color, StyledExt}; pub use crate::{Headline, HeadlineSize}; -pub use crate::{Icon, IconElement, IconPosition, IconSize}; +pub use crate::{Icon, IconName, IconPosition, IconSize}; pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle}; pub use theme::ActiveTheme; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index adc4fb5c80b2fd35af7ae32a620a550c977e469a..ed03695c5f2c234f3e7a633cb341da6002d344cd 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -28,7 +28,7 @@ pub trait Panel: FocusableView + EventEmitter { fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext); fn size(&self, cx: &WindowContext) -> Pixels; fn set_size(&mut self, size: Option, cx: &mut ViewContext); - fn icon(&self, cx: &WindowContext) -> Option; + fn icon(&self, cx: &WindowContext) -> Option; fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str>; fn toggle_action(&self) -> Box; fn icon_label(&self, _: &WindowContext) -> Option { @@ -52,7 +52,7 @@ pub trait PanelHandle: Send + Sync { fn set_active(&self, active: bool, cx: &mut WindowContext); fn size(&self, cx: &WindowContext) -> Pixels; fn set_size(&self, size: Option, cx: &mut WindowContext); - fn icon(&self, cx: &WindowContext) -> Option; + fn icon(&self, cx: &WindowContext) -> Option; fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str>; fn toggle_action(&self, cx: &WindowContext) -> Box; fn icon_label(&self, cx: &WindowContext) -> Option; @@ -104,7 +104,7 @@ where self.update(cx, |this, cx| this.set_size(size, cx)) } - fn icon(&self, cx: &WindowContext) -> Option { + fn icon(&self, cx: &WindowContext) -> Option { self.read(cx).icon(cx) } @@ -774,7 +774,7 @@ pub mod test { self.size = size.unwrap_or(px(300.)); } - fn icon(&self, _: &WindowContext) -> Option { + fn icon(&self, _: &WindowContext) -> Option { None } diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index d70de8c13dc7b45522952474506d609e1a75788b..36628290bb83ed6f640206f20f77933a577f58e3 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -175,7 +175,7 @@ pub mod simple_message_notification { }; use std::sync::Arc; use ui::prelude::*; - use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt}; + use ui::{h_stack, v_stack, Button, Icon, IconName, Label, StyledExt}; pub struct MessageNotification { message: SharedString, @@ -230,7 +230,7 @@ pub mod simple_message_notification { .child( div() .id("cancel") - .child(IconElement::new(Icon::Close)) + .child(Icon::new(IconName::Close)) .cursor_pointer() .on_click(cx.listener(|this, _, cx| this.dismiss(cx))), ), diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 04a51fc655be0d7b5d2b890479bef484b5cbc14a..2a434b32d928bcd7d59de0bca03dad5c8d79d031 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -31,8 +31,8 @@ use std::{ use theme::ThemeSettings; use ui::{ - prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize, Indicator, Label, - Tab, TabBar, TabPosition, Tooltip, + prelude::*, right_click_menu, ButtonSize, Color, IconButton, IconName, IconSize, Indicator, + Label, Tab, TabBar, TabPosition, Tooltip, }; use ui::{v_stack, ContextMenu}; use util::{maybe, truncate_and_remove_front, ResultExt}; @@ -384,7 +384,7 @@ impl Pane { h_stack() .gap_2() .child( - IconButton::new("plus", Icon::Plus) + IconButton::new("plus", IconName::Plus) .icon_size(IconSize::Small) .icon_color(Color::Muted) .on_click(cx.listener(|pane, _, cx| { @@ -406,7 +406,7 @@ impl Pane { el.child(Self::render_menu_overlay(new_item_menu)) }) .child( - IconButton::new("split", Icon::Split) + IconButton::new("split", IconName::Split) .icon_size(IconSize::Small) .icon_color(Color::Muted) .on_click(cx.listener(|pane, _, cx| { @@ -427,11 +427,11 @@ impl Pane { ) .child({ let zoomed = pane.is_zoomed(); - IconButton::new("toggle_zoom", Icon::Maximize) + IconButton::new("toggle_zoom", IconName::Maximize) .icon_size(IconSize::Small) .icon_color(Color::Muted) .selected(zoomed) - .selected_icon(Icon::Minimize) + .selected_icon(IconName::Minimize) .on_click(cx.listener(|pane, _, cx| { pane.toggle_zoom(&crate::ToggleZoom, cx); })) @@ -1570,7 +1570,7 @@ impl Pane { }) .start_slot::(indicator) .end_slot( - IconButton::new("close tab", Icon::Close) + IconButton::new("close tab", IconName::Close) .icon_color(Color::Muted) .size(ButtonSize::None) .icon_size(IconSize::XSmall) @@ -1676,7 +1676,7 @@ impl Pane { h_stack() .gap_2() .child( - IconButton::new("navigate_backward", Icon::ArrowLeft) + IconButton::new("navigate_backward", IconName::ArrowLeft) .icon_size(IconSize::Small) .on_click({ let view = cx.view().clone(); @@ -1686,7 +1686,7 @@ impl Pane { .tooltip(|cx| Tooltip::for_action("Go Back", &GoBack, cx)), ) .child( - IconButton::new("navigate_forward", Icon::ArrowRight) + IconButton::new("navigate_forward", IconName::ArrowRight) .icon_size(IconSize::Small) .on_click({ let view = cx.view().clone(); diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs index edfabed60d3a03e2290fb94dc9c8482a7e9b4a5e..5b1ca6477ee99b47ad7abc08e64e0421444c5dfd 100644 --- a/crates/workspace/src/shared_screen.rs +++ b/crates/workspace/src/shared_screen.rs @@ -12,7 +12,7 @@ use gpui::{ WindowContext, }; use std::sync::{Arc, Weak}; -use ui::{h_stack, prelude::*, Icon, IconElement, Label}; +use ui::{h_stack, prelude::*, Icon, IconName, Label}; pub enum Event { Close, @@ -100,7 +100,7 @@ impl Item for SharedScreen { ) -> gpui::AnyElement { h_stack() .gap_1() - .child(IconElement::new(Icon::Screen)) + .child(Icon::new(IconName::Screen)) .child( Label::new(format!("{}'s screen", self.user.github_login)).color(if selected { Color::Default From f0ef63bfa0a6cab1676d27ae75e839c2e7666e3c Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 9 Jan 2024 10:55:49 -0500 Subject: [PATCH 53/54] gpui: Add `SharedUrl` type (#3975) This PR adds a `SharedUrl` type to GPUI. It's just like a `SharedString`, but for denoting that the contained value is a URL. Mainlined from @nathansobo's GPUI blog post: https://github.com/zed-industries/zed/pull/3968/files#diff-7ee75937e2daf7dd53f71b17698d8bd6d46993d06928d411781b9bd739b5f231R9-R12 Release Notes: - N/A --- crates/client/src/user.rs | 4 +-- .../src/notifications/collab_notification.rs | 6 ++--- crates/gpui/src/elements/img.rs | 8 +++--- crates/gpui/src/gpui.rs | 2 ++ crates/gpui/src/image_cache.rs | 6 ++--- crates/gpui/src/shared_url.rs | 25 +++++++++++++++++++ crates/ui/src/components/stories/list_item.rs | 18 ++++++------- 7 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 crates/gpui/src/shared_url.rs diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 1c288c875db39e3d3d7aed81ed836c1a69b41f5e..4453bb40eaaf34291cfa5b7f9cc917cd8606706f 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, HashMap, HashSet}; use feature_flags::FeatureFlagAppExt; use futures::{channel::mpsc, Future, StreamExt}; -use gpui::{AsyncAppContext, EventEmitter, Model, ModelContext, SharedString, Task}; +use gpui::{AsyncAppContext, EventEmitter, Model, ModelContext, SharedUrl, Task}; use postage::{sink::Sink, watch}; use rpc::proto::{RequestMessage, UsersResponse}; use std::sync::{Arc, Weak}; @@ -19,7 +19,7 @@ pub struct ParticipantIndex(pub u32); pub struct User { pub id: UserId, pub github_login: String, - pub avatar_uri: SharedString, + pub avatar_uri: SharedUrl, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/collab_ui/src/notifications/collab_notification.rs b/crates/collab_ui/src/notifications/collab_notification.rs index e2ef06f9a58f8b131f166ab2a83250839f076ca2..fa0b0a1b14782b8bbe586348487228d75df743f7 100644 --- a/crates/collab_ui/src/notifications/collab_notification.rs +++ b/crates/collab_ui/src/notifications/collab_notification.rs @@ -1,10 +1,10 @@ -use gpui::{img, prelude::*, AnyElement}; +use gpui::{img, prelude::*, AnyElement, SharedUrl}; use smallvec::SmallVec; use ui::prelude::*; #[derive(IntoElement)] pub struct CollabNotification { - avatar_uri: SharedString, + avatar_uri: SharedUrl, accept_button: Button, dismiss_button: Button, children: SmallVec<[AnyElement; 2]>, @@ -12,7 +12,7 @@ pub struct CollabNotification { impl CollabNotification { pub fn new( - avatar_uri: impl Into, + avatar_uri: impl Into, accept_button: Button, dismiss_button: Button, ) -> Self { diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index 650b5b666bc821e15873c53f2cdc56c0546b6a20..71a51351fdb24ccf4b275b42e2541a87569014dd 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ point, size, BorrowWindow, Bounds, DevicePixels, Element, ImageData, InteractiveElement, - InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedString, Size, + InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size, StyleRefinement, Styled, WindowContext, }; use futures::FutureExt; @@ -12,13 +12,13 @@ use util::ResultExt; #[derive(Clone, Debug)] pub enum ImageSource { /// Image content will be loaded from provided URI at render time. - Uri(SharedString), + Uri(SharedUrl), Data(Arc), Surface(CVImageBuffer), } -impl From for ImageSource { - fn from(value: SharedString) -> Self { +impl From for ImageSource { + fn from(value: SharedUrl) -> Self { Self::Uri(value) } } diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index d5236d8f08c7f2fa4cb3551ee5a58695634f7e8a..6f5e30149d9691b3c364d62ab2e3ce6ec7da1b4c 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -18,6 +18,7 @@ mod platform; pub mod prelude; mod scene; mod shared_string; +mod shared_url; mod style; mod styled; mod subscription; @@ -67,6 +68,7 @@ pub use refineable::*; pub use scene::*; use seal::Sealed; pub use shared_string::*; +pub use shared_url::*; pub use smol::Timer; pub use style::*; pub use styled::*; diff --git a/crates/gpui/src/image_cache.rs b/crates/gpui/src/image_cache.rs index f80b0f0c2f71a60fa91dbf87a13ffa3b86f43abf..0d6ec81557aa7b21165a628cf707f0d1caded9d0 100644 --- a/crates/gpui/src/image_cache.rs +++ b/crates/gpui/src/image_cache.rs @@ -1,4 +1,4 @@ -use crate::{ImageData, ImageId, SharedString}; +use crate::{ImageData, ImageId, SharedUrl}; use collections::HashMap; use futures::{ future::{BoxFuture, Shared}, @@ -44,7 +44,7 @@ impl From for Error { pub struct ImageCache { client: Arc, - images: Arc>>, + images: Arc>>, } type FetchImageFuture = Shared, Error>>>; @@ -59,7 +59,7 @@ impl ImageCache { pub fn get( &self, - uri: impl Into, + uri: impl Into, ) -> Shared, Error>>> { let uri = uri.into(); let mut images = self.images.lock(); diff --git a/crates/gpui/src/shared_url.rs b/crates/gpui/src/shared_url.rs new file mode 100644 index 0000000000000000000000000000000000000000..8fb901894367d3fa4d87e78d8b95e1a47df0ce10 --- /dev/null +++ b/crates/gpui/src/shared_url.rs @@ -0,0 +1,25 @@ +use derive_more::{Deref, DerefMut}; + +use crate::SharedString; + +/// A [`SharedString`] containing a URL. +#[derive(Deref, DerefMut, Default, PartialEq, Eq, Hash, Clone)] +pub struct SharedUrl(SharedString); + +impl std::fmt::Debug for SharedUrl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::fmt::Display for SharedUrl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.as_ref()) + } +} + +impl> From for SharedUrl { + fn from(value: T) -> Self { + Self(value.into()) + } +} diff --git a/crates/ui/src/components/stories/list_item.rs b/crates/ui/src/components/stories/list_item.rs index 487f14279178cbaf3d6d4baaab5e5c2f2609eb7a..a25b07df849aeb67a185e3984ab11ec341b07045 100644 --- a/crates/ui/src/components/stories/list_item.rs +++ b/crates/ui/src/components/stories/list_item.rs @@ -1,4 +1,4 @@ -use gpui::Render; +use gpui::{Render, SharedUrl}; use story::Story; use crate::{prelude::*, Avatar}; @@ -43,7 +43,7 @@ impl Render for ListItemStory { .child( ListItem::new("with_start slot avatar") .child("Hello, world!") - .start_slot(Avatar::new(SharedString::from( + .start_slot(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1714999?v=4", ))), ) @@ -51,7 +51,7 @@ impl Render for ListItemStory { .child( ListItem::new("with_left_avatar") .child("Hello, world!") - .end_slot(Avatar::new(SharedString::from( + .end_slot(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1714999?v=4", ))), ) @@ -62,23 +62,23 @@ impl Render for ListItemStory { .end_slot( h_stack() .gap_2() - .child(Avatar::new(SharedString::from( + .child(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1789?v=4", ))) - .child(Avatar::new(SharedString::from( + .child(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1789?v=4", ))) - .child(Avatar::new(SharedString::from( + .child(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1789?v=4", ))) - .child(Avatar::new(SharedString::from( + .child(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1789?v=4", ))) - .child(Avatar::new(SharedString::from( + .child(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1789?v=4", ))), ) - .end_hover_slot(Avatar::new(SharedString::from( + .end_hover_slot(Avatar::new(SharedUrl::from( "https://avatars.githubusercontent.com/u/1714999?v=4", ))), ) From d3749531808f4626b4e790a1eec47d3b4860400b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:16:25 +0100 Subject: [PATCH 54/54] search: Remove newlines from query used for tab_content. (#3976) Fixes https://github.com/zed-industries/community/issues/2388 Release Notes: - Fixed tab content of project search overflowing the tab for queries with newlines. --- crates/search/src/project_search.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 5cdf614c1b9f8e22c416866db3a863e2458251c2..6fd66b5bad2c1c9ab1036a445f1e2061b791f206 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -424,7 +424,8 @@ impl Item for ProjectSearchView { .current() .as_ref() .map(|query| { - let query_text = util::truncate_and_trailoff(query, MAX_TAB_TITLE_LEN); + let query = query.replace('\n', ""); + let query_text = util::truncate_and_trailoff(&query, MAX_TAB_TITLE_LEN); query_text.into() }); let tab_name = last_query