Restyle diagnostic headers

Antonio Scandurra created

Change summary

crates/diagnostics/src/diagnostics.rs | 118 ++++++++++++++++++++++------
crates/editor/src/editor.rs           |  39 +++++++--
crates/theme/src/theme.rs             |  39 +++++++++
crates/zed/assets/themes/_base.toml   |  20 +++
crates/zed/assets/themes/black.toml   |   9 -
5 files changed, 176 insertions(+), 49 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -3,7 +3,7 @@ pub mod items;
 use anyhow::Result;
 use collections::{BTreeSet, HashMap, HashSet};
 use editor::{
-    diagnostic_block_renderer, diagnostic_style,
+    diagnostic_block_renderer,
     display_map::{BlockDisposition, BlockId, BlockProperties, RenderBlock},
     items::BufferItemHandle,
     Autoscroll, BuildSettings, Editor, ExcerptId, ExcerptProperties, MultiBuffer, ToOffset,
@@ -12,7 +12,7 @@ use gpui::{
     action, elements::*, keymap::Binding, AnyViewHandle, AppContext, Entity, ModelHandle,
     MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
 };
-use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point, Selection, SelectionGoal};
+use language::{Bias, Buffer, DiagnosticEntry, Point, Selection, SelectionGoal};
 use postage::watch;
 use project::{Project, ProjectPath};
 use std::{
@@ -345,16 +345,14 @@ impl ProjectDiagnosticsEditor {
                             if is_first_excerpt_for_group {
                                 is_first_excerpt_for_group = false;
                                 let primary = &group.entries[group.primary_ix].diagnostic;
-                                let mut header = primary.clone();
-                                header.message =
+                                let message =
                                     primary.message.split('\n').next().unwrap().to_string();
                                 group_state.block_count += 1;
                                 blocks_to_add.push(BlockProperties {
                                     position: header_position,
                                     height: 2,
                                     render: diagnostic_header_renderer(
-                                        header,
-                                        true,
+                                        message,
                                         self.build_settings.clone(),
                                     ),
                                     disposition: BlockDisposition::Above,
@@ -651,41 +649,105 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
 fn path_header_renderer(buffer: ModelHandle<Buffer>, build_settings: BuildSettings) -> RenderBlock {
     Arc::new(move |cx| {
         let settings = build_settings(cx);
-        let file_path = if let Some(file) = buffer.read(&**cx).file() {
-            file.path().to_string_lossy().to_string()
-        } else {
-            "untitled".to_string()
-        };
-        let mut text_style = settings.style.text.clone();
         let style = settings.style.diagnostic_path_header;
-        text_style.color = style.text;
-        Label::new(file_path, text_style)
+
+        let mut filename = None;
+        let mut path = None;
+        if let Some(file) = buffer.read(&**cx).file() {
+            filename = file
+                .path()
+                .file_name()
+                .map(|f| f.to_string_lossy().to_string());
+            path = file
+                .path()
+                .parent()
+                .map(|p| p.to_string_lossy().to_string());
+        }
+
+        Flex::row()
+            .with_child(
+                Label::new(
+                    filename.unwrap_or_else(|| "untitled".to_string()),
+                    style.filename.text.clone(),
+                )
+                .contained()
+                .with_style(style.filename.container)
+                .boxed(),
+            )
+            .with_children(path.map(|path| {
+                Label::new(path, style.path.text.clone())
+                    .contained()
+                    .with_style(style.path.container)
+                    .boxed()
+            }))
             .aligned()
             .left()
             .contained()
-            .with_style(style.header)
+            .with_style(style.container)
             .with_padding_left(cx.line_number_x)
             .expanded()
             .named("path header block")
     })
 }
 
-fn diagnostic_header_renderer(
-    diagnostic: Diagnostic,
-    is_valid: bool,
-    build_settings: BuildSettings,
-) -> RenderBlock {
+fn diagnostic_header_renderer(message: String, build_settings: BuildSettings) -> RenderBlock {
+    enum Run {
+        Text(Range<usize>),
+        Code(Range<usize>),
+    }
+
+    let mut prev_ix = 0;
+    let mut inside_block = false;
+    let mut runs = Vec::new();
+    for (backtick_ix, _) in message.match_indices('`') {
+        if backtick_ix > prev_ix {
+            if inside_block {
+                runs.push(Run::Code(prev_ix..backtick_ix));
+            } else {
+                runs.push(Run::Text(prev_ix..backtick_ix));
+            }
+        }
+
+        inside_block = !inside_block;
+        prev_ix = backtick_ix + 1;
+    }
+    if prev_ix < message.len() {
+        if inside_block {
+            runs.push(Run::Code(prev_ix..message.len()));
+        } else {
+            runs.push(Run::Text(prev_ix..message.len()));
+        }
+    }
+
     Arc::new(move |cx| {
         let settings = build_settings(cx);
-        let mut text_style = settings.style.text.clone();
-        let diagnostic_style = diagnostic_style(diagnostic.severity, is_valid, &settings.style);
-        text_style.color = diagnostic_style.text;
-        Text::new(diagnostic.message.clone(), text_style)
-            .with_soft_wrap(false)
-            .aligned()
-            .left()
+        let style = &settings.style.diagnostic_header;
+
+        Flex::row()
+            .with_children(runs.iter().map(|run| {
+                let container_style;
+                let text_style;
+                let range;
+                match run {
+                    Run::Text(run_range) => {
+                        container_style = Default::default();
+                        text_style = style.text.clone();
+                        range = run_range.clone();
+                    }
+                    Run::Code(run_range) => {
+                        container_style = style.highlighted_text.container;
+                        text_style = style.highlighted_text.text.clone();
+                        range = run_range.clone();
+                    }
+                }
+                Label::new(message[range].to_string(), text_style)
+                    .contained()
+                    .with_style(container_style)
+                    .aligned()
+                    .boxed()
+            }))
             .contained()
-            .with_style(diagnostic_style.header)
+            .with_style(style.container)
             .with_padding_left(cx.line_number_x)
             .expanded()
             .named("diagnostic header")

crates/editor/src/editor.rs 🔗

@@ -3814,16 +3814,17 @@ impl EditorSettings {
                 let font_id = font_cache
                     .select_font(font_family_id, &font_properties)
                     .unwrap();
+                let text = gpui::fonts::TextStyle {
+                    font_family_name,
+                    font_family_id,
+                    font_id,
+                    font_size: 14.,
+                    color: gpui::color::Color::from_u32(0xff0000ff),
+                    font_properties,
+                    underline: None,
+                };
                 EditorStyle {
-                    text: gpui::fonts::TextStyle {
-                        font_family_name,
-                        font_family_id,
-                        font_id,
-                        font_size: 14.,
-                        color: gpui::color::Color::from_u32(0xff0000ff),
-                        font_properties,
-                        underline: None,
-                    },
+                    text: text.clone(),
                     placeholder_text: None,
                     background: Default::default(),
                     gutter_background: Default::default(),
@@ -3834,7 +3835,25 @@ impl EditorSettings {
                     selection: Default::default(),
                     guest_selections: Default::default(),
                     syntax: Default::default(),
-                    diagnostic_path_header: Default::default(),
+                    diagnostic_path_header: theme::DiagnosticPathHeader {
+                        container: Default::default(),
+                        filename: theme::ContainedText {
+                            container: Default::default(),
+                            text: text.clone(),
+                        },
+                        path: theme::ContainedText {
+                            container: Default::default(),
+                            text: text.clone(),
+                        },
+                    },
+                    diagnostic_header: theme::DiagnosticHeader {
+                        container: Default::default(),
+                        text: text.clone(),
+                        highlighted_text: theme::ContainedText {
+                            container: Default::default(),
+                            text: text.clone(),
+                        },
+                    },
                     error_diagnostic: Default::default(),
                     invalid_error_diagnostic: Default::default(),
                     warning_diagnostic: Default::default(),

crates/theme/src/theme.rs 🔗

@@ -251,7 +251,8 @@ pub struct EditorStyle {
     pub line_number_active: Color,
     pub guest_selections: Vec<SelectionStyle>,
     pub syntax: Arc<SyntaxTheme>,
-    pub diagnostic_path_header: DiagnosticStyle,
+    pub diagnostic_path_header: DiagnosticPathHeader,
+    pub diagnostic_header: DiagnosticHeader,
     pub error_diagnostic: DiagnosticStyle,
     pub invalid_error_diagnostic: DiagnosticStyle,
     pub warning_diagnostic: DiagnosticStyle,
@@ -262,6 +263,22 @@ pub struct EditorStyle {
     pub invalid_hint_diagnostic: DiagnosticStyle,
 }
 
+#[derive(Clone, Deserialize, Default)]
+pub struct DiagnosticPathHeader {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub filename: ContainedText,
+    pub path: ContainedText,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct DiagnosticHeader {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub text: TextStyle,
+    pub highlighted_text: ContainedText,
+}
+
 #[derive(Copy, Clone, Deserialize, Default)]
 pub struct DiagnosticStyle {
     pub text: Color,
@@ -317,7 +334,25 @@ impl InputEditorStyle {
             line_number_active: Default::default(),
             guest_selections: Default::default(),
             syntax: Default::default(),
-            diagnostic_path_header: Default::default(),
+            diagnostic_path_header: DiagnosticPathHeader {
+                container: Default::default(),
+                filename: ContainedText {
+                    container: Default::default(),
+                    text: self.text.clone(),
+                },
+                path: ContainedText {
+                    container: Default::default(),
+                    text: self.text.clone(),
+                },
+            },
+            diagnostic_header: DiagnosticHeader {
+                container: Default::default(),
+                text: self.text.clone(),
+                highlighted_text: ContainedText {
+                    container: Default::default(),
+                    text: self.text.clone(),
+                },
+            },
             error_diagnostic: Default::default(),
             invalid_error_diagnostic: Default::default(),
             warning_diagnostic: Default::default(),

crates/zed/assets/themes/_base.toml 🔗

@@ -187,7 +187,7 @@ corner_radius = 6
 
 [project_panel]
 extends = "$panel"
-padding.top = 6    # ($workspace.tab.height - $project_panel.entry.height) / 2
+padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
 
 [project_panel.entry]
 text = "$text.1"
@@ -257,9 +257,21 @@ invalid_information_diagnostic = { text = "$text.3.color" }
 invalid_hint_diagnostic = { text = "$text.3.color" }
 
 [editor.diagnostic_path_header]
-text = "$text.0.color"
-header.background = "#ffffff08"
-header.border = { width = 1, top = true, color = "$border.0" }
+filename = { extends = "$text.0", size = 14 }
+path = { extends = "$text.1", size = 14, margin.left = 4 }
+
+[editor.diagnostic_header]
+background = "$state.active_line"
+border = { width = 1, top = true, bottom = true, color = "$border.0" }
+text = { extends = "$text.1", size = 14 }
+
+[editor.diagnostic_header.highlighted_text]
+extends = "$editor.diagnostic_header.text"
+color = "$text.0.color"
+background = "#ffffff1f"
+padding.left = 3
+padding.right = 3
+corner_radius = 3
 
 [editor.error_diagnostic]
 text = "$status.bad"

crates/zed/assets/themes/black.toml 🔗

@@ -1,12 +1,12 @@
 extends = "_base"
 
 [surface]
-0 = "#222324"
-1 = "#141516"
+0 = "#222222"
+1 = "#0f0b0c"
 2 = "#131415"
 
 [border]
-0 = "#0F1011"
+0 = "#000000B2"
 
 [text]
 0 = { extends = "$text.base", color = "#ffffff" }
@@ -36,7 +36,7 @@ warn = "#faca50"
 bad = "#b7372e"
 
 [state]
-active_line = "#00000033"
+active_line = "#161313"
 highlighted_line = "#faca5033"
 hover = "#00000033"
 
@@ -50,7 +50,6 @@ comment = "#6a9955"
 property = "#4e94ce"
 variant = "#4fc1ff"
 constant = "#9cdcfe"
-
 title = { color = "#9cdcfe", weight = "bold" }
 emphasis = "#4ec9b0"
 "emphasis.strong" = { color = "#4ec9b0", weight = "bold" }