Update the styling of the lsp status indicator

Max Brunsfeld created

Change summary

assets/icons/download-solid-14.svg   |   3 
assets/themes/cave-dark.json         |  39 ++++++++-
assets/themes/cave-light.json        |  39 ++++++++-
assets/themes/dark.json              |  39 ++++++++-
assets/themes/light.json             |  39 ++++++++-
assets/themes/solarized-dark.json    |  39 ++++++++-
assets/themes/solarized-light.json   |  39 ++++++++-
assets/themes/sulphurpool-dark.json  |  39 ++++++++-
assets/themes/sulphurpool-light.json |  39 ++++++++-
crates/theme/src/theme.rs            |  17 ++++
crates/workspace/src/lsp_status.rs   | 110 ++++++++++++++++++++---------
styles/src/styleTree/workspace.ts    |  22 ++++-
12 files changed, 367 insertions(+), 97 deletions(-)

Detailed changes

assets/icons/download-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 10C3.58579 10 3.25 10.3358 3.25 10.75C3.25 11.1642 3.58579 11.5 4 11.5V10ZM10 11.5C10.4142 11.5 10.75 11.1642 10.75 10.75C10.75 10.3358 10.4142 10 10 10V11.5ZM7.75 3.5C7.75 3.08579 7.41421 2.75 7 2.75C6.58579 2.75 6.25 3.08579 6.25 3.5H7.75ZM7 8L6.51191 8.56944C6.79277 8.81019 7.20723 8.81019 7.48809 8.56944L7 8ZM9.23809 7.06944C9.55259 6.79988 9.58901 6.3264 9.31944 6.01191C9.04988 5.69741 8.5764 5.66099 8.26191 5.93056L9.23809 7.06944ZM5.73809 5.93056C5.4236 5.66099 4.95012 5.69741 4.68056 6.01191C4.41099 6.3264 4.44741 6.79988 4.76191 7.06944L5.73809 5.93056ZM4 11.5H10V10H4V11.5ZM6.25 3.5V8H7.75V3.5H6.25ZM7.48809 8.56944L9.23809 7.06944L8.26191 5.93056L6.51191 7.43056L7.48809 8.56944ZM7.48809 7.43056L5.73809 5.93056L4.76191 7.06944L6.51191 8.56944L7.48809 7.43056Z" fill="white"/>
+</svg>

assets/themes/cave-dark.json 🔗

@@ -194,11 +194,6 @@
         "color": "#8b8792",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#8b8792",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#8b8792",
@@ -209,6 +204,36 @@
         "color": "#8b8792",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#8b8792",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#e2dfe7",
+          "size": 14
+        },
+        "icon_color": "#8b8792",
+        "icon_color_hover": "#e2dfe7",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#58526052"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#a06e3b",
         "icon_color_error": "#be4678",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/cave-light.json 🔗

@@ -194,11 +194,6 @@
         "color": "#585260",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#585260",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#585260",
@@ -209,6 +204,36 @@
         "color": "#585260",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#585260",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#26232a",
+          "size": 14
+        },
+        "icon_color": "#585260",
+        "icon_color_hover": "#26232a",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#8b87921f"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#a06e3b",
         "icon_color_error": "#be4678",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/dark.json 🔗

@@ -194,11 +194,6 @@
         "color": "#808080",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#808080",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#808080",
@@ -209,6 +204,36 @@
         "color": "#808080",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#808080",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#f1f1f1",
+          "size": 14
+        },
+        "icon_color": "#555555",
+        "icon_color_hover": "#c6c6c6",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#232323"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#f6a724",
         "icon_color_error": "#eb2d2d",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/light.json 🔗

@@ -194,11 +194,6 @@
         "color": "#636363",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#636363",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#636363",
@@ -209,6 +204,36 @@
         "color": "#636363",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#636363",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#2b2b2b",
+          "size": 14
+        },
+        "icon_color": "#9c9c9c",
+        "icon_color_hover": "#393939",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#eaeaea"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#f7bf17",
         "icon_color_error": "#c91818",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/solarized-dark.json 🔗

@@ -194,11 +194,6 @@
         "color": "#93a1a1",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#93a1a1",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#93a1a1",
@@ -209,6 +204,36 @@
         "color": "#93a1a1",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#93a1a1",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#eee8d5",
+          "size": 14
+        },
+        "icon_color": "#93a1a1",
+        "icon_color_hover": "#eee8d5",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#586e7552"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#b58900",
         "icon_color_error": "#dc322f",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/solarized-light.json 🔗

@@ -194,11 +194,6 @@
         "color": "#586e75",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#586e75",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#586e75",
@@ -209,6 +204,36 @@
         "color": "#586e75",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#586e75",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#073642",
+          "size": 14
+        },
+        "icon_color": "#586e75",
+        "icon_color_hover": "#073642",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#93a1a11f"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#b58900",
         "icon_color_error": "#dc322f",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/sulphurpool-dark.json 🔗

@@ -194,11 +194,6 @@
         "color": "#979db4",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#979db4",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#979db4",
@@ -209,6 +204,36 @@
         "color": "#979db4",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#979db4",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#dfe2f1",
+          "size": 14
+        },
+        "icon_color": "#979db4",
+        "icon_color_hover": "#dfe2f1",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#5e668752"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#c08b30",
         "icon_color_error": "#c94922",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

assets/themes/sulphurpool-light.json 🔗

@@ -194,11 +194,6 @@
         "color": "#5e6687",
         "size": 14
       },
-      "lsp_message": {
-        "family": "Zed Sans",
-        "color": "#5e6687",
-        "size": 14
-      },
       "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#5e6687",
@@ -209,6 +204,36 @@
         "color": "#5e6687",
         "size": 14
       },
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "message": {
+          "family": "Zed Sans",
+          "color": "#5e6687",
+          "size": 14
+        },
+        "message_hover": {
+          "family": "Zed Sans",
+          "color": "#293256",
+          "size": 14
+        },
+        "icon_color": "#5e6687",
+        "icon_color_hover": "#293256",
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "container_hover": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#979db41f"
+        }
+      },
       "diagnostics": {
         "height": 16,
         "summary_ok": {
@@ -300,8 +325,8 @@
         "icon_color_warning": "#c08b30",
         "icon_color_error": "#c94922",
         "icon_width": 14,
-        "icon_spacing": 4,
-        "summary_spacing": 8
+        "icon_spacing": 2,
+        "summary_spacing": 6
       },
       "sidebar_buttons": {
         "group_left": {},

crates/theme/src/theme.rs 🔗

@@ -143,10 +143,9 @@ pub struct StatusBar {
     pub height: f32,
     pub item_spacing: f32,
     pub cursor_position: TextStyle,
-    pub diagnostic_message: TextStyle,
-    pub lsp_message: TextStyle,
     pub auto_update_progress_message: TextStyle,
     pub auto_update_done_message: TextStyle,
+    pub lsp_status: StatusBarLspStatus,
     pub sidebar_buttons: StatusBarSidebarButtons,
     pub diagnostics: StatusBarDiagnostics,
 }
@@ -178,6 +177,20 @@ pub struct StatusBarDiagnostics {
     pub summary_spacing: f32,
 }
 
+#[derive(Deserialize, Default)]
+pub struct StatusBarLspStatus {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub height: f32,
+    pub container_hover: ContainerStyle,
+    pub icon_spacing: f32,
+    pub icon_color: Color,
+    pub icon_color_hover: Color,
+    pub icon_width: f32,
+    pub message: TextStyle,
+    pub message_hover: TextStyle,
+}
+
 #[derive(Deserialize, Default)]
 pub struct Sidebar {
     pub resize_handle: ContainerStyle,

crates/workspace/src/lsp_status.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{ItemHandle, StatusItemView};
 use futures::StreamExt;
-use gpui::{actions, AppContext};
+use gpui::{actions, AppContext, EventContext};
 use gpui::{
     elements::*, platform::CursorStyle, Entity, ModelHandle, MutableAppContext, RenderContext,
     View, ViewContext,
@@ -117,11 +117,13 @@ impl View for LspStatus {
     }
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme;
+        let mut message;
+        let mut icon = None;
+        let mut handler = None;
 
         let mut pending_work = self.pending_language_server_work(cx);
         if let Some((lang_server_name, progress_token, progress)) = pending_work.next() {
-            let mut message = lang_server_name.to_string();
+            message = lang_server_name.to_string();
 
             message.push_str(": ");
             if let Some(progress_message) = progress.message.as_ref() {
@@ -138,21 +140,19 @@ impl View for LspStatus {
             if additional_work_count > 0 {
                 write!(&mut message, " + {} more", additional_work_count).unwrap();
             }
+        } else {
+            drop(pending_work);
 
-            Label::new(message, theme.workspace.status_bar.lsp_message.clone()).boxed()
-        } else if !self.downloading.is_empty() {
-            Label::new(
-                format!(
+            if !self.downloading.is_empty() {
+                icon = Some("icons/download-solid-14.svg");
+                message = format!(
                     "Downloading {} language server{}...",
                     self.downloading.join(", "),
                     if self.downloading.len() > 1 { "s" } else { "" }
-                ),
-                theme.workspace.status_bar.lsp_message.clone(),
-            )
-            .boxed()
-        } else if !self.checking_for_update.is_empty() {
-            Label::new(
-                format!(
+                );
+            } else if !self.checking_for_update.is_empty() {
+                icon = Some("icons/download-solid-14.svg");
+                message = format!(
                     "Checking for updates to {} language server{}...",
                     self.checking_for_update.join(", "),
                     if self.checking_for_update.len() > 1 {
@@ -160,30 +160,70 @@ impl View for LspStatus {
                     } else {
                         ""
                     }
-                ),
-                theme.workspace.status_bar.lsp_message.clone(),
-            )
-            .boxed()
-        } else if !self.failed.is_empty() {
-            drop(pending_work);
-            MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
-                let theme = &cx.global::<Settings>().theme;
-                Label::new(
-                    format!(
-                        "Failed to download {} language server{}. Click to dismiss.",
-                        self.failed.join(", "),
-                        if self.failed.len() > 1 { "s" } else { "" }
-                    ),
-                    theme.workspace.status_bar.lsp_message.clone(),
+                );
+            } else if !self.failed.is_empty() {
+                icon = Some("icons/warning-solid-14.svg");
+                message = format!(
+                    "Failed to download {} language server{}. Click to dismiss.",
+                    self.failed.join(", "),
+                    if self.failed.len() > 1 { "s" } else { "" }
+                );
+                handler = Some(|cx: &mut EventContext| cx.dispatch_action(DismissErrorMessage));
+            } else {
+                return Empty::new().boxed();
+            }
+        }
+
+        let mut element = MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
+            let hovered = state.hovered && handler.is_some();
+            let theme = &cx.global::<Settings>().theme;
+            let style = &theme.workspace.status_bar.lsp_status;
+            Flex::row()
+                .with_children(icon.map(|path| {
+                    Svg::new(path)
+                        .with_color(if hovered {
+                            style.icon_color_hover
+                        } else {
+                            style.icon_color
+                        })
+                        .constrained()
+                        .with_width(style.icon_width)
+                        .contained()
+                        .with_margin_right(style.icon_spacing)
+                        .aligned()
+                        .named("warning-icon")
+                }))
+                .with_child(
+                    Label::new(
+                        message,
+                        if hovered {
+                            style.message_hover.clone()
+                        } else {
+                            style.message.clone()
+                        },
+                    )
+                    .aligned()
+                    .boxed(),
                 )
+                .constrained()
+                .with_height(style.height)
+                .contained()
+                .with_style(if hovered {
+                    style.container_hover
+                } else {
+                    style.container
+                })
+                .aligned()
                 .boxed()
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(|cx| cx.dispatch_action(DismissErrorMessage))
-            .boxed()
-        } else {
-            Empty::new().boxed()
+        });
+
+        if let Some(handler) = handler {
+            element = element
+                .with_cursor_style(CursorStyle::PointingHand)
+                .on_click(handler);
         }
+
+        element.boxed()
     }
 }
 

styles/src/styleTree/workspace.ts 🔗

@@ -1,5 +1,4 @@
 import Theme from "../themes/theme";
-import { color } from "../tokens";
 import { backgroundColor, border, iconColor, text } from "./components";
 
 export default function workspace(theme: Theme) {
@@ -97,9 +96,24 @@ export default function workspace(theme: Theme) {
       border: border(theme, "primary", { top: true, overlay: true }),
       cursorPosition: text(theme, "sans", "muted"),
       diagnosticMessage: text(theme, "sans", "muted"),
-      lspMessage: text(theme, "sans", "muted"),
       autoUpdateProgressMessage: text(theme, "sans", "muted"),
       autoUpdateDoneMessage: text(theme, "sans", "muted"),
+      lspStatus: {
+        iconSpacing: 4,
+        iconWidth: 14,
+        height: 18,
+        message: text(theme, "sans", "muted"),
+        messageHover: text(theme, "sans", "primary"),
+        iconColor: iconColor(theme, "muted"),
+        iconColorHover: iconColor(theme, "primary"),
+        cornerRadius: 6,
+        padding: { left: 6, right: 6 },
+        containerHover: {
+          cornerRadius: 6,
+          padding: { left: 6, right: 6 },
+          background: backgroundColor(theme, 300, "hovered"),
+        }
+      },
       diagnostics: {
         height: 16,
         summaryOk: {
@@ -133,8 +147,8 @@ export default function workspace(theme: Theme) {
         iconColorWarning: iconColor(theme, "warning"),
         iconColorError: iconColor(theme, "error"),
         iconWidth: 14,
-        iconSpacing: 4,
-        summarySpacing: 8,
+        iconSpacing: 2,
+        summarySpacing: 6,
       },
       sidebarButtons: {
         groupLeft: {},