From ce4c15dca6f3885ddc7f2df64ca259b282b5a769 Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:10:42 +0100 Subject: [PATCH] Show diagnostics in scrollbar (#7175) This PR implements support for displaying diagnostics in the scrollbar, similar to what is already done for search results, symbols, git diff, ... For example, changing a field name (`text`) without changing the references looks like this in `buffer.rs` (note the red lines in the scrollbar): ![image](https://github.com/zed-industries/zed/assets/53836821/c46f0d55-32e3-4334-8ad7-66d1578d5725) As you can see, the errors, warnings, ... are displayed in the scroll bar, which helps to identify possible problems with the current file. Relevant issues: #4866, #6819 Release Notes: - Added diagnostic indicators to the scrollbar --- assets/settings/default.json | 4 +- crates/editor/src/editor_settings.rs | 5 ++ crates/editor/src/element.rs | 62 +++++++++++++++++++++++++ crates/language/src/buffer.rs | 5 ++ crates/multi_buffer/src/multi_buffer.rs | 6 +++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index f78a4637e5..74dfa6de2c 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -127,7 +127,9 @@ // Whether to show selections in the scrollbar. "selections": true, // Whether to show symbols selections in the scrollbar. - "symbols_selections": true + "symbols_selections": true, + // Whether to show diagnostic indicators in the scrollbar. + "diagnostics": true }, "relative_line_numbers": false, // When to populate a new search's query based on the text under the cursor. diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 0cfc677e93..e9ecbe13d4 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -34,6 +34,7 @@ pub struct Scrollbar { pub git_diff: bool, pub selections: bool, pub symbols_selections: bool, + pub diagnostics: bool, } /// When to show the scrollbar in the editor. @@ -122,6 +123,10 @@ pub struct ScrollbarContent { /// /// Default: true pub symbols_selections: Option, + /// Whether to show diagnostic indicators in the scrollbar. + /// + /// Default: true + pub diagnostics: Option, } impl Settings for EditorSettings { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 57389fd8f9..b96ec9e068 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -35,6 +35,7 @@ use gpui::{ }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; +use lsp::DiagnosticSeverity; use multi_buffer::Anchor; use project::{ project_settings::{GitGutterSetting, ProjectSettings}, @@ -1477,6 +1478,64 @@ impl EditorElement { } } + if layout.is_singleton && scrollbar_settings.diagnostics { + let max_point = layout + .position_map + .snapshot + .display_snapshot + .buffer_snapshot + .max_point(); + + let diagnostics = layout + .position_map + .snapshot + .buffer_snapshot + .diagnostics_in_range::<_, Point>(Point::zero()..max_point, false) + // We want to sort by severity, in order to paint the most severe diagnostics last. + .sorted_by_key(|diagnostic| std::cmp::Reverse(diagnostic.diagnostic.severity)); + + for diagnostic in diagnostics { + let start_display = diagnostic + .range + .start + .to_display_point(&layout.position_map.snapshot.display_snapshot); + let end_display = diagnostic + .range + .end + .to_display_point(&layout.position_map.snapshot.display_snapshot); + let start_y = y_for_row(start_display.row() as f32); + let mut end_y = if diagnostic.range.start == diagnostic.range.end { + y_for_row((end_display.row() + 1) as f32) + } else { + y_for_row((end_display.row()) as f32) + }; + + if end_y - start_y < px(1.) { + end_y = start_y + px(1.); + } + let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y)); + + let color = match diagnostic.diagnostic.severity { + DiagnosticSeverity::ERROR => cx.theme().status().error, + DiagnosticSeverity::WARNING => cx.theme().status().warning, + DiagnosticSeverity::INFORMATION => cx.theme().status().info, + _ => cx.theme().status().hint, + }; + cx.paint_quad(quad( + bounds, + Corners::default(), + color, + Edges { + top: Pixels::ZERO, + right: px(1.), + bottom: Pixels::ZERO, + left: px(1.), + }, + cx.theme().colors().scrollbar_thumb_border, + )); + } + } + cx.paint_quad(quad( thumb_bounds, Corners::default(), @@ -2106,6 +2165,9 @@ impl EditorElement { // Symbols Selections (is_singleton && scrollbar_settings.symbols_selections && (editor.has_background_highlights::() || editor.has_background_highlights::())) || + // Diagnostics + (is_singleton && scrollbar_settings.diagnostics && snapshot.buffer_snapshot.has_diagnostics()) + || // Scrollmanager editor.scroll_manager.scrollbars_visible() } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index cd8b239f5c..b42ea1f12d 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2993,6 +2993,11 @@ impl BufferSnapshot { self.git_diff.hunks_intersecting_range_rev(range, self) } + /// Returns if the buffer contains any diagnostics. + pub fn has_diagnostics(&self) -> bool { + !self.diagnostics.is_empty() + } + /// Returns all the diagnostics intersecting the given range. pub fn diagnostics_in_range<'a, T, O>( &'a self, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e16fc3a504..6d182a11ea 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -3052,6 +3052,12 @@ impl MultiBufferSnapshot { self.has_conflict } + pub fn has_diagnostics(&self) -> bool { + self.excerpts + .iter() + .any(|excerpt| excerpt.buffer.has_diagnostics()) + } + pub fn diagnostic_group<'a, O>( &'a self, group_id: usize,