Detailed changes
@@ -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,105 +37,87 @@ 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)
- } else if entry.range.start.row.abs_diff(primary.range.start.row) < 5 {
- close.push(entry)
- } else {
- distant.push((ix, entry))
- }
- }
-
- 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 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(')');
+ }
- 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 (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 {
+ let markdown = Self::markdown(&entry.diagnostic);
- for entry in close {
- let markdown = if let Some(source) = entry.diagnostic.source.as_ref() {
- format!("{}: {}", source, entry.diagnostic.message)
+ 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 {
- entry.diagnostic.message
- };
- let markdown = Markdown::escape(&markdown).to_string();
+ 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,
- severity: entry.diagnostic.severity,
- diagnostics_editor: diagnostics_editor.clone(),
- markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
- });
+ 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)),
+ });
+ }
}
- 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
+ }
- 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)),
- });
- }
+ fn markdown(diagnostic: &Diagnostic) -> String {
+ let mut markdown = String::new();
- results
+ if let Some(md) = &diagnostic.markdown {
+ markdown.push_str(md);
+ } else {
+ markdown.push_str(&Markdown::escape(&diagnostic.message));
+ };
+ markdown
}
}
@@ -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),
@@ -416,7 +416,6 @@ impl ProjectDiagnosticsEditor {
group,
buffer_snapshot.remote_id(),
Some(this.clone()),
- true,
cx,
)
})?;
@@ -633,7 +633,7 @@ pub fn hover_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
base_text_style,
code_block: StyleRefinement::default().my(rems(1.)).font_buffer(cx),
inline_code: TextStyleRefinement {
- background_color: Some(cx.theme().colors().background),
+ background_color: Some(cx.theme().colors().editor_background.opacity(0.5)),
font_family: Some(buffer_font_family),
font_fallbacks: buffer_font_fallbacks,
..Default::default()
@@ -213,6 +213,8 @@ pub struct Diagnostic {
pub severity: DiagnosticSeverity,
/// The human-readable message associated with this diagnostic.
pub message: String,
+ /// The human-readable message (in markdown format)
+ pub markdown: Option<String>,
/// An id that identifies the group to which this diagnostic belongs.
///
/// When a language server produces a diagnostic with
@@ -4616,6 +4618,7 @@ impl Default for Diagnostic {
code_description: None,
severity: DiagnosticSeverity::ERROR,
message: Default::default(),
+ markdown: None,
group_id: 0,
is_primary: false,
is_disk_based: false,
@@ -239,6 +239,10 @@ impl CachedLspAdapter {
.process_diagnostics(params, server_id, existing_diagnostics)
}
+ pub fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
+ self.adapter.diagnostic_message_to_markdown(message)
+ }
+
pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) {
self.adapter.process_completions(completion_items).await
}
@@ -460,6 +464,10 @@ pub trait LspAdapter: 'static + Send + Sync {
/// Post-processes completions provided by the language server.
async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {}
+ fn diagnostic_message_to_markdown(&self, _message: &str) -> Option<String> {
+ None
+ }
+
async fn labels_for_completions(
self: Arc<Self>,
completions: &[lsp::CompletionItem],
@@ -203,6 +203,7 @@ pub fn serialize_diagnostics<'a>(
start: Some(serialize_anchor(&entry.range.start)),
end: Some(serialize_anchor(&entry.range.end)),
message: entry.diagnostic.message.clone(),
+ markdown: entry.diagnostic.markdown.clone(),
severity: match entry.diagnostic.severity {
DiagnosticSeverity::ERROR => proto::diagnostic::Severity::Error,
DiagnosticSeverity::WARNING => proto::diagnostic::Severity::Warning,
@@ -422,6 +423,7 @@ pub fn deserialize_diagnostics(
proto::diagnostic::Severity::None => return None,
},
message: diagnostic.message,
+ markdown: diagnostic.markdown,
group_id: diagnostic.group_id as usize,
code: diagnostic.code.map(lsp::NumberOrString::from_string),
code_description: diagnostic
@@ -283,6 +283,12 @@ impl LspAdapter for RustLspAdapter {
}
}
+ fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
+ static REGEX: LazyLock<Regex> =
+ LazyLock::new(|| Regex::new(r"(?m)\n *").expect("Failed to create REGEX"));
+ Some(REGEX.replace_all(message, "\n\n").to_string())
+ }
+
async fn label_for_completion(
&self,
completion: &lsp::CompletionItem,
@@ -8575,6 +8575,8 @@ impl LspStore {
let mut sources_by_group_id = HashMap::default();
let mut supporting_diagnostics = HashMap::default();
+ let adapter = self.language_server_adapter_for_id(language_server_id);
+
// Ensure that primary diagnostics are always the most severe
params.diagnostics.sort_by_key(|item| item.severity);
@@ -8622,6 +8624,9 @@ impl LspStore {
.as_ref()
.map(|d| d.href.clone()),
severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
+ markdown: adapter.as_ref().and_then(|adapter| {
+ adapter.diagnostic_message_to_markdown(&diagnostic.message)
+ }),
message: diagnostic.message.trim().to_string(),
group_id,
is_primary: true,
@@ -8644,6 +8649,9 @@ impl LspStore {
.as_ref()
.map(|c| c.href.clone()),
severity: DiagnosticSeverity::INFORMATION,
+ markdown: adapter.as_ref().and_then(|adapter| {
+ adapter.diagnostic_message_to_markdown(&info.message)
+ }),
message: info.message.trim().to_string(),
group_id,
is_primary: false,
@@ -271,6 +271,7 @@ message Diagnostic {
}
optional string data = 12;
optional string code_description = 13;
+ optional string markdown = 14;
}
message SearchQuery {