Tidy up diagnostics more (#29629)

- Stop merging same row diagnostics
- (for Rust) show code fragments surrounded by `'s in monospace

Co-authored-by: Serge Radinovich <sergeradinovich@gmail.com>

Closes #29362

Release Notes:

- diagnostics: Diagnostics are no longer merged when they're on the same
line
- rust: Diagnostics now show code snippets in monospace font:

<img width="551" alt="Screenshot 2025-04-29 at 16 13 45"
src="https://github.com/user-attachments/assets/d289be31-717d-404f-a76a-a0cda3e96fbe"
/>

Co-authored-by: Serge Radinovich <sergeradinovich@gmail.com>
This commit is contained in:
Conrad Irwin 2025-04-29 19:53:05 -06:00 committed by GitHub
parent b4732235e3
commit e364e48266
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 106 additions and 99 deletions

View file

@ -7,7 +7,7 @@ use editor::{
scroll::Autoscroll,
};
use gpui::{AppContext, Entity, Focusable, WeakEntity};
use language::{BufferId, DiagnosticEntry};
use language::{BufferId, Diagnostic, DiagnosticEntry};
use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownElement};
use settings::Settings;
@ -28,7 +28,6 @@ impl DiagnosticRenderer {
diagnostic_group: Vec<DiagnosticEntry<Point>>,
buffer_id: BufferId,
diagnostics_editor: Option<WeakEntity<ProjectDiagnosticsEditor>>,
merge_same_row: bool,
cx: &mut App,
) -> Vec<DiagnosticBlock> {
let Some(primary_ix) = diagnostic_group
@ -38,106 +37,88 @@ impl DiagnosticRenderer {
return Vec::new();
};
let primary = diagnostic_group[primary_ix].clone();
let mut same_row = Vec::new();
let mut close = Vec::new();
let mut distant = Vec::new();
let group_id = primary.diagnostic.group_id;
for (ix, entry) in diagnostic_group.into_iter().enumerate() {
let mut results = vec![];
for entry in diagnostic_group.iter() {
if entry.diagnostic.is_primary {
continue;
}
if entry.range.start.row == primary.range.start.row && merge_same_row {
same_row.push(entry)
let mut markdown = Self::markdown(&entry.diagnostic);
let diagnostic = &primary.diagnostic;
if diagnostic.source.is_some() || diagnostic.code.is_some() {
markdown.push_str(" (");
}
if let Some(source) = diagnostic.source.as_ref() {
markdown.push_str(&Markdown::escape(&source));
}
if diagnostic.source.is_some() && diagnostic.code.is_some() {
markdown.push(' ');
}
if let Some(code) = diagnostic.code.as_ref() {
if let Some(description) = diagnostic.code_description.as_ref() {
markdown.push('[');
markdown.push_str(&Markdown::escape(&code.to_string()));
markdown.push_str("](");
markdown.push_str(&Markdown::escape(description.as_ref()));
markdown.push(')');
} else {
markdown.push_str(&Markdown::escape(&code.to_string()));
}
}
if diagnostic.source.is_some() || diagnostic.code.is_some() {
markdown.push(')');
}
for (ix, entry) in diagnostic_group.iter().enumerate() {
if entry.range.start.row.abs_diff(primary.range.start.row) >= 5 {
markdown.push_str("\n- hint: [");
markdown.push_str(&Markdown::escape(&entry.diagnostic.message));
markdown.push_str(&format!(
"](file://#diagnostic-{buffer_id}-{group_id}-{ix})\n",
))
}
}
results.push(DiagnosticBlock {
initial_range: primary.range.clone(),
severity: primary.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
});
} else if entry.range.start.row.abs_diff(primary.range.start.row) < 5 {
close.push(entry)
let markdown = Self::markdown(&entry.diagnostic);
results.push(DiagnosticBlock {
initial_range: entry.range.clone(),
severity: entry.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
});
} else {
distant.push((ix, entry))
let mut markdown = Self::markdown(&entry.diagnostic);
markdown.push_str(&format!(
" ([back](file://#diagnostic-{buffer_id}-{group_id}-{primary_ix}))"
));
results.push(DiagnosticBlock {
initial_range: entry.range.clone(),
severity: entry.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
});
}
}
let mut markdown = String::new();
let diagnostic = &primary.diagnostic;
markdown.push_str(&Markdown::escape(&diagnostic.message));
for entry in same_row {
markdown.push_str("\n- hint: ");
markdown.push_str(&Markdown::escape(&entry.diagnostic.message))
}
if diagnostic.source.is_some() || diagnostic.code.is_some() {
markdown.push_str(" (");
}
if let Some(source) = diagnostic.source.as_ref() {
markdown.push_str(&Markdown::escape(&source));
}
if diagnostic.source.is_some() && diagnostic.code.is_some() {
markdown.push(' ');
}
if let Some(code) = diagnostic.code.as_ref() {
if let Some(description) = diagnostic.code_description.as_ref() {
markdown.push('[');
markdown.push_str(&Markdown::escape(&code.to_string()));
markdown.push_str("](");
markdown.push_str(&Markdown::escape(description.as_ref()));
markdown.push(')');
} else {
markdown.push_str(&Markdown::escape(&code.to_string()));
}
}
if diagnostic.source.is_some() || diagnostic.code.is_some() {
markdown.push(')');
}
for (ix, entry) in &distant {
markdown.push_str("\n- hint: [");
markdown.push_str(&Markdown::escape(&entry.diagnostic.message));
markdown.push_str(&format!(
"](file://#diagnostic-{buffer_id}-{group_id}-{ix})\n",
))
}
let mut results = vec![DiagnosticBlock {
initial_range: primary.range,
severity: primary.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
}];
for entry in close {
let markdown = if let Some(source) = entry.diagnostic.source.as_ref() {
format!("{}: {}", source, entry.diagnostic.message)
} else {
entry.diagnostic.message
};
let markdown = Markdown::escape(&markdown).to_string();
results.push(DiagnosticBlock {
initial_range: entry.range,
severity: entry.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
});
}
for (_, entry) in distant {
let markdown = if let Some(source) = entry.diagnostic.source.as_ref() {
format!("{}: {}", source, entry.diagnostic.message)
} else {
entry.diagnostic.message
};
let mut markdown = Markdown::escape(&markdown).to_string();
markdown.push_str(&format!(
" ([back](file://#diagnostic-{buffer_id}-{group_id}-{primary_ix}))"
));
results.push(DiagnosticBlock {
initial_range: entry.range,
severity: entry.diagnostic.severity,
diagnostics_editor: diagnostics_editor.clone(),
markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
});
}
results
}
fn markdown(diagnostic: &Diagnostic) -> String {
let mut markdown = String::new();
if let Some(md) = &diagnostic.markdown {
markdown.push_str(md);
} else {
markdown.push_str(&Markdown::escape(&diagnostic.message));
};
markdown
}
}
impl editor::DiagnosticRenderer for DiagnosticRenderer {
@ -149,7 +130,7 @@ impl editor::DiagnosticRenderer for DiagnosticRenderer {
editor: WeakEntity<Editor>,
cx: &mut App,
) -> Vec<BlockProperties<Anchor>> {
let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, true, cx);
let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx);
blocks
.into_iter()
.map(|block| {
@ -176,8 +157,7 @@ impl editor::DiagnosticRenderer for DiagnosticRenderer {
buffer_id: BufferId,
cx: &mut App,
) -> Option<Entity<Markdown>> {
let blocks =
Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, false, cx);
let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx);
blocks.into_iter().find_map(|block| {
if block.initial_range == range {
Some(block.markdown)
@ -211,7 +191,7 @@ impl DiagnosticBlock {
let cx = &bcx.app;
let status_colors = bcx.app.theme().status();
let max_width = bcx.em_width * 100.;
let max_width = bcx.em_width * 120.;
let (background_color, border_color) = match self.severity {
DiagnosticSeverity::ERROR => (status_colors.error_background, status_colors.error),