From 2823771c0632f4a4c73e754c9d37689ae74699be Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:27:21 -0300 Subject: [PATCH] Add design improvements to the LSP popover (#33485) Not the ideal design just yet as that will probably require a different approach altogether, but am pushing here just some reasonably small UI adjustments that will make this feel slightly nicer! Release Notes: - N/A --- assets/icons/bolt_filled_alt.svg | 3 + assets/icons/lsp_debug.svg | 12 +++ assets/icons/lsp_restart.svg | 4 + assets/icons/lsp_stop.svg | 4 + crates/icons/src/icons.rs | 4 + crates/language_tools/src/lsp_tool.rs | 132 ++++++++++++-------------- crates/zed/src/zed.rs | 2 +- 7 files changed, 90 insertions(+), 71 deletions(-) create mode 100644 assets/icons/bolt_filled_alt.svg create mode 100644 assets/icons/lsp_debug.svg create mode 100644 assets/icons/lsp_restart.svg create mode 100644 assets/icons/lsp_stop.svg diff --git a/assets/icons/bolt_filled_alt.svg b/assets/icons/bolt_filled_alt.svg new file mode 100644 index 0000000000..3c89387362 --- /dev/null +++ b/assets/icons/bolt_filled_alt.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/lsp_debug.svg b/assets/icons/lsp_debug.svg new file mode 100644 index 0000000000..aa49fcb6a2 --- /dev/null +++ b/assets/icons/lsp_debug.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/lsp_restart.svg b/assets/icons/lsp_restart.svg new file mode 100644 index 0000000000..dfc68e7a9e --- /dev/null +++ b/assets/icons/lsp_restart.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/lsp_stop.svg b/assets/icons/lsp_stop.svg new file mode 100644 index 0000000000..c6311d2155 --- /dev/null +++ b/assets/icons/lsp_stop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs index 7e1d7db575..ffbe148a3b 100644 --- a/crates/icons/src/icons.rs +++ b/crates/icons/src/icons.rs @@ -45,6 +45,7 @@ pub enum IconName { Blocks, Bolt, BoltFilled, + BoltFilledAlt, Book, BookCopy, BookPlus, @@ -163,6 +164,9 @@ pub enum IconName { ListX, LoadCircle, LockOutlined, + LspDebug, + LspRestart, + LspStop, MagnifyingGlass, MailOpen, Maximize, diff --git a/crates/language_tools/src/lsp_tool.rs b/crates/language_tools/src/lsp_tool.rs index a7bfe70aaf..140f9e3fe6 100644 --- a/crates/language_tools/src/lsp_tool.rs +++ b/crates/language_tools/src/lsp_tool.rs @@ -10,7 +10,7 @@ use lsp::{LanguageServerId, LanguageServerName, LanguageServerSelector}; use picker::{Picker, PickerDelegate, popover_menu::PickerPopoverMenu}; use project::{LspStore, LspStoreEvent, project_settings::ProjectSettings}; use settings::{Settings as _, SettingsStore}; -use ui::{Context, IconButtonShape, Indicator, Tooltip, Window, prelude::*}; +use ui::{Context, Indicator, Tooltip, Window, prelude::*}; use workspace::{StatusItemView, Workspace}; @@ -181,16 +181,19 @@ impl LspPickerDelegate { buffer_servers.sort_by_key(|data| data.name().clone()); other_servers.sort_by_key(|data| data.name().clone()); + let mut other_servers_start_index = None; let mut new_lsp_items = Vec::with_capacity(buffer_servers.len() + other_servers.len() + 2); + if !buffer_servers.is_empty() { - new_lsp_items.push(LspItem::Header(SharedString::new("Current Buffer"))); + new_lsp_items.push(LspItem::Header(SharedString::new("This Buffer"))); new_lsp_items.extend(buffer_servers.into_iter().map(ServerData::into_lsp_item)); } + if !other_servers.is_empty() { other_servers_start_index = Some(new_lsp_items.len()); - new_lsp_items.push(LspItem::Header(SharedString::new("Other Active Servers"))); + new_lsp_items.push(LspItem::Header(SharedString::new("Other Servers"))); new_lsp_items.extend(other_servers.into_iter().map(ServerData::into_lsp_item)); } @@ -346,11 +349,13 @@ impl PickerDelegate for LspPickerDelegate { let is_other_server = self .other_servers_start_index .map_or(false, |start| ix >= start); + let server_binary_status; let server_health; let server_message; let server_id; let server_name; + match self.items.get(ix)? { LspItem::WithHealthCheck( language_server_id, @@ -372,9 +377,14 @@ impl PickerDelegate for LspPickerDelegate { } LspItem::Header(header) => { return Some( - h_flex() - .justify_center() - .child(Label::new(header.clone())) + div() + .px_2p5() + .mb_1() + .child( + Label::new(header.clone()) + .size(LabelSize::Small) + .color(Color::Muted), + ) .into_any_element(), ); } @@ -389,10 +399,12 @@ impl PickerDelegate for LspPickerDelegate { let can_stop = server_binary_status.is_none_or(|status| { matches!(status.status, BinaryStatus::None | BinaryStatus::Starting) }); + // TODO currently, Zed remote does not work well with the LSP logs // https://github.com/zed-industries/zed/issues/28557 let has_logs = lsp_store.read(cx).as_local().is_some() && lsp_logs.read(cx).has_server_logs(&server_selector); + let status_color = server_binary_status .and_then(|binary_status| match binary_status.status { BinaryStatus::None => None, @@ -414,27 +426,28 @@ impl PickerDelegate for LspPickerDelegate { Some( h_flex() - .w_full() + .px_1() + .gap_1() .justify_between() - .gap_2() .child( h_flex() .id("server-status-indicator") + .px_2() .gap_2() .child(Indicator::dot().color(status_color)) .child(Label::new(server_name.0.clone())) .when_some(server_message.clone(), |div, server_message| { - div.tooltip(move |_, cx| Tooltip::simple(server_message.clone(), cx)) + div.tooltip(Tooltip::text(server_message.clone())) }), ) .child( h_flex() - .gap_1() - .when(has_logs, |div| { - div.child( - IconButton::new("debug-language-server", IconName::MessageBubbles) - .icon_size(IconSize::XSmall) - .tooltip(|_, cx| Tooltip::simple("Debug Language Server", cx)) + .when(has_logs, |button_list| { + button_list.child( + IconButton::new("debug-language-server", IconName::LspDebug) + .icon_size(IconSize::Small) + .alpha(0.8) + .tooltip(Tooltip::text("Debug Language Server")) .on_click({ let workspace = workspace.clone(); let lsp_logs = lsp_logs.downgrade(); @@ -454,11 +467,12 @@ impl PickerDelegate for LspPickerDelegate { }), ) }) - .when(can_stop, |div| { - div.child( - IconButton::new("stop-server", IconName::Stop) + .when(can_stop, |button_list| { + button_list.child( + IconButton::new("stop-server", IconName::LspStop) .icon_size(IconSize::Small) - .tooltip(|_, cx| Tooltip::simple("Stop server", cx)) + .alpha(0.8) + .tooltip(Tooltip::text("Stop Server")) .on_click({ let lsp_store = lsp_store.downgrade(); let server_selector = server_selector.clone(); @@ -479,9 +493,10 @@ impl PickerDelegate for LspPickerDelegate { ) }) .child( - IconButton::new("restart-server", IconName::Rerun) - .icon_size(IconSize::XSmall) - .tooltip(|_, cx| Tooltip::simple("Restart server", cx)) + IconButton::new("restart-server", IconName::LspRestart) + .icon_size(IconSize::Small) + .alpha(0.8) + .tooltip(Tooltip::text("Restart Server")) .on_click({ let state = self.state.clone(); let workspace = workspace.clone(); @@ -558,7 +573,6 @@ impl PickerDelegate for LspPickerDelegate { }), ), ) - .cursor_default() .into_any_element(), ) } @@ -573,49 +587,28 @@ impl PickerDelegate for LspPickerDelegate { } fn render_footer(&self, _: &mut Window, cx: &mut Context>) -> Option { - if self.items.is_empty() { - Some( - h_flex() - .w_full() - .border_color(cx.theme().colors().border_variant) - .child( - Button::new("stop-all-servers", "Stop all servers") - .disabled(true) - .on_click(move |_, _, _| {}) - .full_width(), - ) - .into_any_element(), - ) - } else { - let lsp_store = self.state.read(cx).lsp_store.clone(); - Some( - h_flex() - .w_full() - .border_color(cx.theme().colors().border_variant) - .child( - Button::new("stop-all-servers", "Stop all servers") - .on_click({ - move |_, _, cx| { - lsp_store - .update(cx, |lsp_store, cx| { - lsp_store.stop_all_language_servers(cx); - }) - .ok(); - } - }) - .full_width(), - ) - .into_any_element(), - ) - } - } + let lsp_store = self.state.read(cx).lsp_store.clone(); - fn separators_after_indices(&self) -> Vec { - if self.items.is_empty() { - Vec::new() - } else { - vec![self.items.len() - 1] - } + Some( + div() + .p_1() + .border_t_1() + .border_color(cx.theme().colors().border_variant) + .child( + Button::new("stop-all-servers", "Stop All Servers") + .disabled(self.items.is_empty()) + .on_click({ + move |_, _, cx| { + lsp_store + .update(cx, |lsp_store, cx| { + lsp_store.stop_all_language_servers(cx); + }) + .ok(); + } + }), + ) + .into_any_element(), + ) } } @@ -911,13 +904,12 @@ impl Render for LspTool { div().child( PickerPopoverMenu::new( lsp_picker.clone(), - IconButton::new("zed-lsp-tool-button", IconName::Bolt) + IconButton::new("zed-lsp-tool-button", IconName::BoltFilledAlt) .when_some(indicator, IconButton::indicator) - .shape(IconButtonShape::Square) - .icon_size(IconSize::XSmall) + .icon_size(IconSize::Small) .indicator_border_color(Some(cx.theme().colors().status_bar_background)), - move |_, cx| Tooltip::simple("Language servers", cx), - Corner::BottomRight, + move |_, cx| Tooltip::simple("Language Servers", cx), + Corner::BottomLeft, cx, ) .render(window, cx), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c57a9b576a..5ab4b672ae 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -332,8 +332,8 @@ pub fn initialize_workspace( cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace)); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(search_button, window, cx); - status_bar.add_left_item(diagnostic_summary, window, cx); status_bar.add_left_item(lsp_tool, window, cx); + status_bar.add_left_item(diagnostic_summary, window, cx); status_bar.add_left_item(activity_indicator, window, cx); status_bar.add_right_item(edit_prediction_button, window, cx); status_bar.add_right_item(active_buffer_language, window, cx);