crates/diagnostics/src/diagnostics.rs 🔗
@@ -4,7 +4,7 @@ mod toolbar_controls;
#[cfg(test)]
mod diagnostics_tests;
-mod grouped_diagnostics;
+pub(crate) mod grouped_diagnostics;
use anyhow::Result;
use collections::{BTreeSet, HashSet};
Kirill Bulatov created
Follow-up of https://github.com/zed-industries/zed/pull/14515
Fixed certain visual artifacts, related to multi line diagnostics and
block toggle rendering.
Also enabled diagnostics toolbar controls for the experimental view too.
Release Notes:
- N/A
crates/diagnostics/src/diagnostics.rs | 2
crates/diagnostics/src/grouped_diagnostics.rs | 123 +++++++++++---------
crates/diagnostics/src/toolbar_controls.rs | 98 +++++++++++----
crates/editor/src/editor.rs | 24 ++-
4 files changed, 152 insertions(+), 95 deletions(-)
@@ -4,7 +4,7 @@ mod toolbar_controls;
#[cfg(test)]
mod diagnostics_tests;
-mod grouped_diagnostics;
+pub(crate) mod grouped_diagnostics;
use anyhow::Result;
use collections::{BTreeSet, HashSet};
@@ -51,18 +51,18 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
-struct GroupedDiagnosticsEditor {
- project: Model<Project>,
+pub struct GroupedDiagnosticsEditor {
+ pub project: Model<Project>,
workspace: WeakView<Workspace>,
focus_handle: FocusHandle,
editor: View<Editor>,
summary: DiagnosticSummary,
excerpts: Model<MultiBuffer>,
path_states: Vec<PathState>,
- paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>,
- include_warnings: bool,
+ pub paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>,
+ pub include_warnings: bool,
context: u32,
- update_paths_tx: UnboundedSender<(ProjectPath, Option<LanguageServerId>)>,
+ pub update_paths_tx: UnboundedSender<(ProjectPath, Option<LanguageServerId>)>,
_update_excerpts_task: Task<Result<()>>,
_subscription: Subscription,
}
@@ -260,7 +260,7 @@ impl GroupedDiagnosticsEditor {
}
}
- fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
+ pub fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
self.include_warnings = !self.include_warnings;
self.enqueue_update_all_excerpts(cx);
cx.notify();
@@ -297,7 +297,7 @@ impl GroupedDiagnosticsEditor {
/// to have changed. If a language server id is passed, then only the excerpts for
/// that language server's diagnostics will be updated. Otherwise, all stale excerpts
/// will be refreshed.
- fn enqueue_update_stale_excerpts(&mut self, language_server_id: Option<LanguageServerId>) {
+ pub fn enqueue_update_stale_excerpts(&mut self, language_server_id: Option<LanguageServerId>) {
for (path, server_id) in &self.paths_to_update {
if language_server_id.map_or(true, |id| id == *server_id) {
self.update_paths_tx
@@ -320,7 +320,7 @@ impl GroupedDiagnosticsEditor {
});
// TODO kb change selections as in the old panel, to the next primary diagnostics
- // TODO kb make [shift-]f8 to work, jump to the next block group
+ // TODO make [shift-]f8 to work, jump to the next block group
let _was_empty = self.path_states.is_empty();
let path_ix = match self.path_states.binary_search_by(|probe| {
project::compare_paths((&probe.path.path, true), (&path_to_update.path, true))
@@ -340,7 +340,6 @@ impl GroupedDiagnosticsEditor {
}
};
- // TODO kb when warnings are turned off, there's a lot of refresh for many paths happening, why?
let max_severity = if self.include_warnings {
DiagnosticSeverity::WARNING
} else {
@@ -633,7 +632,7 @@ fn compare_diagnostic_ranges(
})
}
-// TODO kb wrong? What to do here instead?
+// TODO wrong? What to do here instead?
fn compare_diagnostic_range_edges(
old: &Range<language::Anchor>,
new: &Range<language::Anchor>,
@@ -1316,59 +1315,67 @@ fn render_same_line_diagnostics(
.map(|diagnostic| diagnostic_text_lines(diagnostic))
.sum::<u8>();
let editor_handle = editor_handle.clone();
- let mut parent = v_flex();
- let mut diagnostics_iter = diagnostics.iter().fuse();
- if let Some(first_diagnostic) = diagnostics_iter.next() {
- let mut renderer = diagnostic_block_renderer(
- first_diagnostic.clone(),
- Some(folded_block_height),
- false,
- true,
- );
- parent = parent.child(
- h_flex()
- .when_some(toggle_expand_label, |parent, label| {
- parent.child(Button::new(cx.transform_block_id, label).on_click({
- let diagnostics = Arc::clone(&diagnostics);
- move |_, cx| {
- let new_expanded = !expanded;
- button_expanded.store(new_expanded, atomic::Ordering::Release);
- let new_size = if new_expanded {
- expanded_block_height
- } else {
- folded_block_height
- };
- editor_handle.update(cx, |editor, cx| {
- editor.replace_blocks(
- HashMap::from_iter(Some((
- block_id,
- (
- Some(new_size),
- render_same_line_diagnostics(
- Arc::clone(&button_expanded),
- Arc::clone(&diagnostics),
- editor_handle.clone(),
- folded_block_height,
- ),
+ let parent = h_flex()
+ .items_start()
+ .child(v_flex().size_full().when_some_else(
+ toggle_expand_label,
+ |parent, label| {
+ parent.child(Button::new(cx.transform_block_id, label).on_click({
+ let diagnostics = Arc::clone(&diagnostics);
+ move |_, cx| {
+ let new_expanded = !expanded;
+ button_expanded.store(new_expanded, atomic::Ordering::Release);
+ let new_size = if new_expanded {
+ expanded_block_height
+ } else {
+ folded_block_height
+ };
+ editor_handle.update(cx, |editor, cx| {
+ editor.replace_blocks(
+ HashMap::from_iter(Some((
+ block_id,
+ (
+ Some(new_size),
+ render_same_line_diagnostics(
+ Arc::clone(&button_expanded),
+ Arc::clone(&diagnostics),
+ editor_handle.clone(),
+ folded_block_height,
),
- ))),
- None,
- cx,
- )
- });
- }
- }))
- })
- .child(renderer(cx)),
- );
- }
+ ),
+ ))),
+ None,
+ cx,
+ )
+ });
+ }
+ }))
+ },
+ |parent| {
+ parent.child(
+ h_flex()
+ .size(IconSize::default().rems())
+ .invisible()
+ .flex_none(),
+ )
+ },
+ ));
+ let max_message_rows = if expanded {
+ None
+ } else {
+ Some(folded_block_height)
+ };
+ let mut renderer =
+ diagnostic_block_renderer(first_diagnostic.clone(), max_message_rows, false, true);
+ let mut diagnostics_element = v_flex();
+ diagnostics_element = diagnostics_element.child(renderer(cx));
if expanded {
- for diagnostic in diagnostics_iter {
+ for diagnostic in diagnostics.iter().skip(1) {
let mut renderer = diagnostic_block_renderer(diagnostic.clone(), None, false, true);
- parent = parent.child(renderer(cx));
+ diagnostics_element = diagnostics_element.child(renderer(cx));
}
}
- parent.into_any_element()
+ parent.child(diagnostics_element).into_any_element()
})
}
@@ -1,11 +1,12 @@
-use crate::ProjectDiagnosticsEditor;
-use gpui::{EventEmitter, ParentElement, Render, ViewContext, WeakView};
+use crate::{grouped_diagnostics::GroupedDiagnosticsEditor, ProjectDiagnosticsEditor};
+use futures::future::Either;
+use gpui::{EventEmitter, ParentElement, Render, View, ViewContext, WeakView};
use ui::prelude::*;
use ui::{IconButton, IconName, Tooltip};
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub struct ToolbarControls {
- editor: Option<WeakView<ProjectDiagnosticsEditor>>,
+ editor: Option<Either<WeakView<ProjectDiagnosticsEditor>, WeakView<GroupedDiagnosticsEditor>>>,
}
impl Render for ToolbarControls {
@@ -14,18 +15,33 @@ impl Render for ToolbarControls {
let mut has_stale_excerpts = false;
let mut is_updating = false;
- if let Some(editor) = self.editor.as_ref().and_then(|editor| editor.upgrade()) {
- let editor = editor.read(cx);
-
- include_warnings = editor.include_warnings;
- has_stale_excerpts = !editor.paths_to_update.is_empty();
- is_updating = editor.update_paths_tx.len() > 0
- || editor
- .project
- .read(cx)
- .language_servers_running_disk_based_diagnostics()
- .next()
- .is_some();
+ if let Some(editor) = self.editor() {
+ match editor {
+ Either::Left(editor) => {
+ let editor = editor.read(cx);
+ include_warnings = editor.include_warnings;
+ has_stale_excerpts = !editor.paths_to_update.is_empty();
+ is_updating = editor.update_paths_tx.len() > 0
+ || editor
+ .project
+ .read(cx)
+ .language_servers_running_disk_based_diagnostics()
+ .next()
+ .is_some();
+ }
+ Either::Right(editor) => {
+ let editor = editor.read(cx);
+ include_warnings = editor.include_warnings;
+ has_stale_excerpts = !editor.paths_to_update.is_empty();
+ is_updating = editor.update_paths_tx.len() > 0
+ || editor
+ .project
+ .read(cx)
+ .language_servers_running_disk_based_diagnostics()
+ .next()
+ .is_some();
+ }
+ }
}
let tooltip = if include_warnings {
@@ -42,12 +58,19 @@ impl Render for ToolbarControls {
.disabled(is_updating)
.tooltip(move |cx| Tooltip::text("Update excerpts", cx))
.on_click(cx.listener(|this, _, cx| {
- if let Some(editor) =
- this.editor.as_ref().and_then(|editor| editor.upgrade())
- {
- editor.update(cx, |editor, _| {
- editor.enqueue_update_stale_excerpts(None);
- });
+ if let Some(editor) = this.editor() {
+ match editor {
+ Either::Left(editor) => {
+ editor.update(cx, |editor, _| {
+ editor.enqueue_update_stale_excerpts(None);
+ });
+ }
+ Either::Right(editor) => {
+ editor.update(cx, |editor, _| {
+ editor.enqueue_update_stale_excerpts(None);
+ });
+ }
+ }
}
})),
)
@@ -56,12 +79,19 @@ impl Render for ToolbarControls {
IconButton::new("toggle-warnings", IconName::ExclamationTriangle)
.tooltip(move |cx| Tooltip::text(tooltip, cx))
.on_click(cx.listener(|this, _, cx| {
- if let Some(editor) =
- this.editor.as_ref().and_then(|editor| editor.upgrade())
- {
- editor.update(cx, |editor, cx| {
- editor.toggle_warnings(&Default::default(), cx);
- });
+ if let Some(editor) = this.editor() {
+ match editor {
+ Either::Left(editor) => {
+ editor.update(cx, |editor, cx| {
+ editor.toggle_warnings(&Default::default(), cx);
+ });
+ }
+ Either::Right(editor) => {
+ editor.update(cx, |editor, cx| {
+ editor.toggle_warnings(&Default::default(), cx);
+ });
+ }
+ }
}
})),
)
@@ -78,7 +108,10 @@ impl ToolbarItemView for ToolbarControls {
) -> ToolbarItemLocation {
if let Some(pane_item) = active_pane_item.as_ref() {
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
- self.editor = Some(editor.downgrade());
+ self.editor = Some(Either::Left(editor.downgrade()));
+ ToolbarItemLocation::PrimaryRight
+ } else if let Some(editor) = pane_item.downcast::<GroupedDiagnosticsEditor>() {
+ self.editor = Some(Either::Right(editor.downgrade()));
ToolbarItemLocation::PrimaryRight
} else {
ToolbarItemLocation::Hidden
@@ -93,4 +126,13 @@ impl ToolbarControls {
pub fn new() -> Self {
ToolbarControls { editor: None }
}
+
+ fn editor(
+ &self,
+ ) -> Option<Either<View<ProjectDiagnosticsEditor>, View<GroupedDiagnosticsEditor>>> {
+ Some(match self.editor.as_ref()? {
+ Either::Left(diagnostics) => Either::Left(diagnostics.upgrade()?),
+ Either::Right(grouped_diagnostics) => Either::Right(grouped_diagnostics.upgrade()?),
+ })
+ }
}
@@ -12827,23 +12827,31 @@ pub fn highlight_diagnostic_message(
let mut prev_offset = 0;
let mut in_code_block = false;
+ let has_row_limit = max_message_rows.is_some();
let mut newline_indices = diagnostic
.message
.match_indices('\n')
+ .filter(|_| has_row_limit)
.map(|(ix, _)| ix)
.fuse()
.peekable();
- for (ix, _) in diagnostic
+
+ for (quote_ix, _) in diagnostic
.message
.match_indices('`')
.chain([(diagnostic.message.len(), "")])
{
- let mut trimmed_ix = ix;
- while let Some(newline_index) = newline_indices.peek() {
- if *newline_index < ix {
+ let mut first_newline_ix = None;
+ let mut last_newline_ix = None;
+ while let Some(newline_ix) = newline_indices.peek() {
+ if *newline_ix < quote_ix {
+ if first_newline_ix.is_none() {
+ first_newline_ix = Some(*newline_ix);
+ }
+ last_newline_ix = Some(*newline_ix);
+
if let Some(rows_left) = &mut max_message_rows {
if *rows_left == 0 {
- trimmed_ix = newline_index.saturating_sub(1);
break;
} else {
*rows_left -= 1;
@@ -12855,14 +12863,14 @@ pub fn highlight_diagnostic_message(
}
}
let prev_len = text_without_backticks.len();
- let new_text = &diagnostic.message[prev_offset..trimmed_ix];
+ let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
text_without_backticks.push_str(new_text);
if in_code_block {
code_ranges.push(prev_len..text_without_backticks.len());
}
- prev_offset = trimmed_ix + 1;
+ prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
in_code_block = !in_code_block;
- if trimmed_ix != ix {
+ if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
text_without_backticks.push_str("...");
break;
}