In a diagnostic group, mark the highest-severity diagnostic as primary

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-11-18 15:53:00 +01:00
parent 401b59be5c
commit 0e51365770
7 changed files with 100 additions and 63 deletions

View File

@ -2205,7 +2205,7 @@ impl Editor {
}
pub fn show_next_diagnostic(&mut self, _: &ShowNextDiagnostic, cx: &mut ViewContext<Self>) {
let selection = self.selections::<usize>(cx).last().unwrap();
let selection = self.newest_selection(cx);
let buffer = self.buffer.read(cx.as_ref());
let diagnostic_group_id = dbg!(buffer
.diagnostics_in_range::<_, usize>(selection.head()..buffer.len())

View File

@ -86,6 +86,7 @@ pub struct Diagnostic {
pub severity: DiagnosticSeverity,
pub message: String,
pub group_id: usize,
pub is_primary: bool,
}
struct LanguageServerState {
@ -717,68 +718,82 @@ impl Buffer {
.peekable();
let mut last_edit_old_end = PointUtf16::zero();
let mut last_edit_new_end = PointUtf16::zero();
let mut groups = HashMap::new();
let mut group_ids_by_diagnostic_range = HashMap::new();
let mut diagnostics_by_group_id = HashMap::new();
let mut next_group_id = 0;
content.anchor_range_multimap(
Bias::Left,
Bias::Right,
diagnostics.iter().filter_map(|diagnostic| {
let mut start = diagnostic.range.start.to_point_utf16();
let mut end = diagnostic.range.end.to_point_utf16();
let source = diagnostic.source.as_ref();
let code = diagnostic.code.as_ref();
let group_id = diagnostic_ranges(&diagnostic, abs_path.as_deref())
.find_map(|range| groups.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.as_deref()) {
groups.insert((source, code, range), group_id);
}
group_id
});
if diagnostic
.source
.as_ref()
.map_or(false, |source| disk_based_sources.contains(source))
{
while let Some(edit) = edits_since_save.peek() {
if edit.old.end <= start {
last_edit_old_end = edit.old.end;
last_edit_new_end = edit.new.end;
edits_since_save.next();
} else if edit.old.start <= end && edit.old.end >= start {
return None;
} else {
break;
}
'outer: for diagnostic in &diagnostics {
let mut start = diagnostic.range.start.to_point_utf16();
let mut end = diagnostic.range.end.to_point_utf16();
let source = diagnostic.source.as_ref();
let code = diagnostic.code.as_ref();
let group_id = diagnostic_ranges(&diagnostic, abs_path.as_deref())
.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.as_deref()) {
group_ids_by_diagnostic_range.insert((source, code, range), group_id);
}
group_id
});
start = last_edit_new_end + (start - last_edit_old_end);
end = last_edit_new_end + (end - last_edit_old_end);
}
let mut range = content.clip_point_utf16(start, Bias::Left)
..content.clip_point_utf16(end, Bias::Right);
if range.start == range.end {
range.end.column += 1;
range.end = content.clip_point_utf16(range.end, Bias::Right);
if range.start == range.end && range.end.column > 0 {
range.start.column -= 1;
range.start = content.clip_point_utf16(range.start, Bias::Left);
if diagnostic
.source
.as_ref()
.map_or(false, |source| disk_based_sources.contains(source))
{
while let Some(edit) = edits_since_save.peek() {
if edit.old.end <= start {
last_edit_old_end = edit.old.end;
last_edit_new_end = edit.new.end;
edits_since_save.next();
} else if edit.old.start <= end && edit.old.end >= start {
continue 'outer;
} else {
break;
}
}
Some((
start = last_edit_new_end + (start - last_edit_old_end);
end = last_edit_new_end + (end - last_edit_old_end);
}
let mut range = content.clip_point_utf16(start, Bias::Left)
..content.clip_point_utf16(end, Bias::Right);
if range.start == range.end {
range.end.column += 1;
range.end = content.clip_point_utf16(range.end, Bias::Right);
if range.start == range.end && range.end.column > 0 {
range.start.column -= 1;
range.start = content.clip_point_utf16(range.start, Bias::Left);
}
}
diagnostics_by_group_id
.entry(group_id)
.or_insert(Vec::new())
.push((
range,
Diagnostic {
severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
message: diagnostic.message.clone(),
group_id,
is_primary: false,
},
))
}),
));
}
content.anchor_range_multimap(
Bias::Left,
Bias::Right,
diagnostics_by_group_id
.into_values()
.flat_map(|mut diagnostics| {
let primary_diagnostic =
diagnostics.iter_mut().min_by_key(|d| d.1.severity).unwrap();
primary_diagnostic.1.is_primary = true;
diagnostics
}),
)
};

View File

@ -142,6 +142,7 @@ pub fn serialize_diagnostics(map: &AnchorRangeMultimap<Diagnostic>) -> proto::Di
_ => proto::diagnostic::Severity::None,
} as i32,
group_id: diagnostic.group_id as u64,
is_primary: diagnostic.is_primary,
})
.collect(),
}
@ -310,6 +311,7 @@ pub fn deserialize_diagnostics(message: proto::DiagnosticSet) -> AnchorRangeMult
},
message: diagnostic.message,
group_id: diagnostic.group_id as usize,
is_primary: diagnostic.is_primary,
},
))
}),

View File

@ -484,6 +484,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::ERROR,
message: "undefined variable 'BB'".to_string(),
group_id: 1,
is_primary: true,
},
),
(
@ -492,6 +493,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::ERROR,
message: "undefined variable 'CCC'".to_string(),
group_id: 2,
is_primary: true,
}
)
]
@ -549,6 +551,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::WARNING,
message: "unreachable statement".to_string(),
group_id: 1,
is_primary: true,
}
),
(
@ -557,6 +560,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::ERROR,
message: "undefined variable 'A'".to_string(),
group_id: 0,
is_primary: true,
},
)
]
@ -626,6 +630,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::ERROR,
message: "undefined variable 'A'".to_string(),
group_id: 0,
is_primary: true,
}
),
(
@ -634,6 +639,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
severity: DiagnosticSeverity::ERROR,
message: "undefined variable 'BB'".to_string(),
group_id: 1,
is_primary: true,
},
)
]
@ -808,7 +814,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::WARNING,
message: "error 1".to_string(),
group_id: 0
group_id: 0,
is_primary: true,
}
),
(
@ -816,7 +823,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 1 hint 1".to_string(),
group_id: 0
group_id: 0,
is_primary: false,
}
),
(
@ -824,7 +832,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 2 hint 1".to_string(),
group_id: 1
group_id: 1,
is_primary: false,
}
),
(
@ -832,7 +841,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 2 hint 2".to_string(),
group_id: 1
group_id: 1,
is_primary: false,
}
),
(
@ -840,7 +850,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::ERROR,
message: "error 2".to_string(),
group_id: 1
group_id: 1,
is_primary: true,
}
)
]
@ -854,7 +865,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::WARNING,
message: "error 1".to_string(),
group_id: 0
group_id: 0,
is_primary: true,
}
),
(
@ -862,7 +874,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 1 hint 1".to_string(),
group_id: 0
group_id: 0,
is_primary: false,
}
),
]
@ -875,7 +888,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 2 hint 1".to_string(),
group_id: 1
group_id: 1,
is_primary: false,
}
),
(
@ -883,7 +897,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::HINT,
message: "error 2 hint 2".to_string(),
group_id: 1
group_id: 1,
is_primary: false,
}
),
(
@ -891,7 +906,8 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
&Diagnostic {
severity: DiagnosticSeverity::ERROR,
message: "error 2".to_string(),
group_id: 1
group_id: 1,
is_primary: true,
}
)
]

View File

@ -3635,6 +3635,7 @@ mod tests {
severity: lsp::DiagnosticSeverity::ERROR,
message: "undefined variable 'A'".to_string(),
group_id: 0,
is_primary: true
}
)]
)

View File

@ -257,6 +257,7 @@ message Diagnostic {
Severity severity = 3;
string message = 4;
uint64 group_id = 5;
bool is_primary = 6;
enum Severity {
None = 0;
Error = 1;

View File

@ -1716,6 +1716,7 @@ mod tests {
group_id: 0,
message: "message 1".to_string(),
severity: lsp::DiagnosticSeverity::ERROR,
is_primary: true
}
),
(
@ -1723,7 +1724,8 @@ mod tests {
&Diagnostic {
group_id: 1,
severity: lsp::DiagnosticSeverity::WARNING,
message: "message 2".to_string()
message: "message 2".to_string(),
is_primary: true
}
)
]