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},
|
ffi::{OsStr, OsString},
|
||||||
fmt,
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
mem,
|
|
||||||
ops::{Deref, Range},
|
ops::{Deref, Range},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -691,7 +690,7 @@ impl Worktree {
|
||||||
|
|
||||||
pub fn update_diagnostics(
|
pub fn update_diagnostics(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut params: lsp::PublishDiagnosticsParams,
|
params: lsp::PublishDiagnosticsParams,
|
||||||
disk_based_sources: &HashSet<String>,
|
disk_based_sources: &HashSet<String>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -706,58 +705,92 @@ impl Worktree {
|
||||||
.context("path is not within 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;
|
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 source = diagnostic.source.as_ref();
|
||||||
let code = diagnostic.code.as_ref();
|
let code = diagnostic.code.as_ref().map(|code| match code {
|
||||||
let group_id = diagnostic_ranges(&diagnostic, &abs_path)
|
lsp::NumberOrString::Number(code) => code.to_string(),
|
||||||
.find_map(|range| group_ids_by_diagnostic_range.get(&(source, code, range)))
|
lsp::NumberOrString::String(code) => code.clone(),
|
||||||
.copied()
|
});
|
||||||
.unwrap_or_else(|| {
|
let range = range_from_lsp(diagnostic.range);
|
||||||
let group_id = post_inc(&mut next_group_id);
|
let is_supporting = diagnostic
|
||||||
for range in diagnostic_ranges(&diagnostic, &abs_path) {
|
.related_information
|
||||||
group_ids_by_diagnostic_range.insert((source, code, range), group_id);
|
.as_ref()
|
||||||
}
|
.map_or(false, |infos| {
|
||||||
group_id
|
infos.iter().any(|info| {
|
||||||
|
primary_diagnostic_group_ids.contains_key(&(
|
||||||
|
source,
|
||||||
|
code.clone(),
|
||||||
|
range_from_lsp(info.location.range),
|
||||||
|
))
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
diagnostics_by_group_id
|
if is_supporting {
|
||||||
.entry(group_id)
|
if let Some(severity) = diagnostic.severity {
|
||||||
.or_insert(Vec::new())
|
supporting_diagnostic_severities
|
||||||
.push(DiagnosticEntry {
|
.insert((source, code.clone(), range), severity);
|
||||||
range: diagnostic.range.start.to_point_utf16()
|
}
|
||||||
..diagnostic.range.end.to_point_utf16(),
|
} 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 {
|
diagnostic: Diagnostic {
|
||||||
code: diagnostic.code.clone().map(|code| match code {
|
code: code.clone(),
|
||||||
lsp::NumberOrString::Number(code) => code.to_string(),
|
|
||||||
lsp::NumberOrString::String(code) => code,
|
|
||||||
}),
|
|
||||||
severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
|
severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
|
||||||
message: mem::take(&mut diagnostic.message),
|
message: diagnostic.message.clone(),
|
||||||
group_id,
|
group_id,
|
||||||
is_primary: false,
|
is_primary: true,
|
||||||
is_valid: true,
|
is_valid: true,
|
||||||
is_disk_based: diagnostic
|
is_disk_based,
|
||||||
.source
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |source| disk_based_sources.contains(source)),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
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
|
for entry in &mut diagnostics {
|
||||||
.into_values()
|
let diagnostic = &mut entry.diagnostic;
|
||||||
.flat_map(|mut diagnostics| {
|
if !diagnostic.is_primary {
|
||||||
let primary = diagnostics
|
let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
|
||||||
.iter_mut()
|
if let Some(&severity) = supporting_diagnostic_severities.get(&(
|
||||||
.min_by_key(|entry| entry.diagnostic.severity)
|
source,
|
||||||
.unwrap();
|
diagnostic.code.clone(),
|
||||||
primary.diagnostic.is_primary = true;
|
entry.range.clone(),
|
||||||
diagnostics
|
)) {
|
||||||
})
|
diagnostic.severity = severity;
|
||||||
.collect::<Vec<_>>();
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.update_diagnostic_entries(worktree_path, params.version, diagnostics, cx)?;
|
self.update_diagnostic_entries(worktree_path, params.version, diagnostics, cx)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3103,32 +3136,10 @@ impl ToPointUtf16 for lsp::Position {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diagnostic_ranges<'a>(
|
fn range_from_lsp(range: lsp::Range) -> Range<PointUtf16> {
|
||||||
diagnostic: &'a lsp::Diagnostic,
|
let start = PointUtf16::new(range.start.line, range.start.character);
|
||||||
abs_path: &'a Path,
|
let end = PointUtf16::new(range.end.line, range.end.character);
|
||||||
) -> impl 'a + Iterator<Item = Range<PointUtf16>> {
|
start..end
|
||||||
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(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue