Always group diagnostics the way they're grouped in the LSP message
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
943571af2a
commit
10548c2038
1 changed files with 79 additions and 68 deletions
|
@ -34,7 +34,6 @@ use std::{
|
|||
ffi::{OsStr, OsString},
|
||||
fmt,
|
||||
future::Future,
|
||||
mem,
|
||||
ops::{Deref, Range},
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
|
@ -691,7 +690,7 @@ impl Worktree {
|
|||
|
||||
pub fn update_diagnostics(
|
||||
&mut self,
|
||||
mut params: lsp::PublishDiagnosticsParams,
|
||||
params: lsp::PublishDiagnosticsParams,
|
||||
disk_based_sources: &HashSet<String>,
|
||||
cx: &mut ModelContext<Worktree>,
|
||||
) -> Result<()> {
|
||||
|
@ -706,58 +705,92 @@ impl Worktree {
|
|||
.context("path is not within worktree")?,
|
||||
);
|
||||
|
||||
let mut group_ids_by_diagnostic_range = HashMap::default();
|
||||
let mut diagnostics_by_group_id = HashMap::default();
|
||||
let mut next_group_id = 0;
|
||||
for diagnostic in &mut params.diagnostics {
|
||||
let mut diagnostics = Vec::default();
|
||||
let mut primary_diagnostic_group_ids = HashMap::default();
|
||||
let mut sources_by_group_id = HashMap::default();
|
||||
let mut supporting_diagnostic_severities = HashMap::default();
|
||||
for diagnostic in ¶ms.diagnostics {
|
||||
let source = diagnostic.source.as_ref();
|
||||
let code = diagnostic.code.as_ref();
|
||||
let group_id = diagnostic_ranges(&diagnostic, &abs_path)
|
||||
.find_map(|range| group_ids_by_diagnostic_range.get(&(source, code, range)))
|
||||
.copied()
|
||||
.unwrap_or_else(|| {
|
||||
let group_id = post_inc(&mut next_group_id);
|
||||
for range in diagnostic_ranges(&diagnostic, &abs_path) {
|
||||
group_ids_by_diagnostic_range.insert((source, code, range), group_id);
|
||||
}
|
||||
group_id
|
||||
let code = diagnostic.code.as_ref().map(|code| match code {
|
||||
lsp::NumberOrString::Number(code) => code.to_string(),
|
||||
lsp::NumberOrString::String(code) => code.clone(),
|
||||
});
|
||||
let range = range_from_lsp(diagnostic.range);
|
||||
let is_supporting = diagnostic
|
||||
.related_information
|
||||
.as_ref()
|
||||
.map_or(false, |infos| {
|
||||
infos.iter().any(|info| {
|
||||
primary_diagnostic_group_ids.contains_key(&(
|
||||
source,
|
||||
code.clone(),
|
||||
range_from_lsp(info.location.range),
|
||||
))
|
||||
})
|
||||
});
|
||||
|
||||
diagnostics_by_group_id
|
||||
.entry(group_id)
|
||||
.or_insert(Vec::new())
|
||||
.push(DiagnosticEntry {
|
||||
range: diagnostic.range.start.to_point_utf16()
|
||||
..diagnostic.range.end.to_point_utf16(),
|
||||
if is_supporting {
|
||||
if let Some(severity) = diagnostic.severity {
|
||||
supporting_diagnostic_severities
|
||||
.insert((source, code.clone(), range), severity);
|
||||
}
|
||||
} else {
|
||||
let group_id = post_inc(&mut next_group_id);
|
||||
let is_disk_based =
|
||||
source.map_or(false, |source| disk_based_sources.contains(source));
|
||||
|
||||
sources_by_group_id.insert(group_id, source);
|
||||
primary_diagnostic_group_ids
|
||||
.insert((source, code.clone(), range.clone()), group_id);
|
||||
|
||||
diagnostics.push(DiagnosticEntry {
|
||||
range,
|
||||
diagnostic: Diagnostic {
|
||||
code: diagnostic.code.clone().map(|code| match code {
|
||||
lsp::NumberOrString::Number(code) => code.to_string(),
|
||||
lsp::NumberOrString::String(code) => code,
|
||||
}),
|
||||
code: code.clone(),
|
||||
severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
|
||||
message: mem::take(&mut diagnostic.message),
|
||||
message: diagnostic.message.clone(),
|
||||
group_id,
|
||||
is_primary: false,
|
||||
is_primary: true,
|
||||
is_valid: true,
|
||||
is_disk_based: diagnostic
|
||||
.source
|
||||
.as_ref()
|
||||
.map_or(false, |source| disk_based_sources.contains(source)),
|
||||
is_disk_based,
|
||||
},
|
||||
});
|
||||
if let Some(infos) = &diagnostic.related_information {
|
||||
for info in infos {
|
||||
if info.location.uri == params.uri {
|
||||
let range = range_from_lsp(info.location.range);
|
||||
diagnostics.push(DiagnosticEntry {
|
||||
range,
|
||||
diagnostic: Diagnostic {
|
||||
code: code.clone(),
|
||||
severity: DiagnosticSeverity::INFORMATION,
|
||||
message: info.message.clone(),
|
||||
group_id,
|
||||
is_primary: false,
|
||||
is_valid: true,
|
||||
is_disk_based,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let diagnostics = diagnostics_by_group_id
|
||||
.into_values()
|
||||
.flat_map(|mut diagnostics| {
|
||||
let primary = diagnostics
|
||||
.iter_mut()
|
||||
.min_by_key(|entry| entry.diagnostic.severity)
|
||||
.unwrap();
|
||||
primary.diagnostic.is_primary = true;
|
||||
diagnostics
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for entry in &mut diagnostics {
|
||||
let diagnostic = &mut entry.diagnostic;
|
||||
if !diagnostic.is_primary {
|
||||
let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
|
||||
if let Some(&severity) = supporting_diagnostic_severities.get(&(
|
||||
source,
|
||||
diagnostic.code.clone(),
|
||||
entry.range.clone(),
|
||||
)) {
|
||||
diagnostic.severity = severity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.update_diagnostic_entries(worktree_path, params.version, diagnostics, cx)?;
|
||||
Ok(())
|
||||
|
@ -3103,32 +3136,10 @@ impl ToPointUtf16 for lsp::Position {
|
|||
}
|
||||
}
|
||||
|
||||
fn diagnostic_ranges<'a>(
|
||||
diagnostic: &'a lsp::Diagnostic,
|
||||
abs_path: &'a Path,
|
||||
) -> impl 'a + Iterator<Item = Range<PointUtf16>> {
|
||||
diagnostic
|
||||
.related_information
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter_map(move |info| {
|
||||
if info.location.uri.to_file_path().ok()? == abs_path {
|
||||
let info_start = PointUtf16::new(
|
||||
info.location.range.start.line,
|
||||
info.location.range.start.character,
|
||||
);
|
||||
let info_end = PointUtf16::new(
|
||||
info.location.range.end.line,
|
||||
info.location.range.end.character,
|
||||
);
|
||||
Some(info_start..info_end)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.chain(Some(
|
||||
diagnostic.range.start.to_point_utf16()..diagnostic.range.end.to_point_utf16(),
|
||||
))
|
||||
fn range_from_lsp(range: lsp::Range) -> Range<PointUtf16> {
|
||||
let start = PointUtf16::new(range.start.line, range.start.character);
|
||||
let end = PointUtf16::new(range.end.line, range.end.character);
|
||||
start..end
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue