Show diagnostics in scrollbar (#7175)
Bennet Bo Fenner
created 2 years ago
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):

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
Detailed changes
@@ -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.
@@ -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<bool>,
+ /// Whether to show diagnostic indicators in the scrollbar.
+ ///
+ /// Default: true
+ pub diagnostics: Option<bool>,
}
impl Settings for EditorSettings {
@@ -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::<DocumentHighlightRead>() || editor.has_background_highlights::<DocumentHighlightWrite>()))
||
+ // Diagnostics
+ (is_singleton && scrollbar_settings.diagnostics && snapshot.buffer_snapshot.has_diagnostics())
+ ||
// Scrollmanager
editor.scroll_manager.scrollbars_visible()
}
@@ -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,
@@ -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,