From 1544da887e3c43ef5aa365e0750a3051f96b38b3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 22 Dec 2021 12:52:41 -0800 Subject: [PATCH] Start work on preserving continuity of disk-based diagnostics --- crates/language/src/buffer.rs | 67 ++++++++++++++++++++++++++++++++-- crates/language/src/proto.rs | 2 + crates/project/src/worktree.rs | 1 + crates/rpc/proto/zed.proto | 10 +++-- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index c09f27bfe3937a5ec6c03aceb2d003e5fad3689b..3d28ce19a8742be83b7ed232566d2d9538794e66 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -18,11 +18,11 @@ use smol::future::yield_now; use std::{ any::Any, cell::RefCell, - cmp, + cmp::{self, Reverse}, collections::{BTreeMap, HashMap, HashSet}, ffi::OsString, future::Future, - iter::{Iterator, Peekable}, + iter::{self, Iterator, Peekable}, ops::{Deref, DerefMut, Range}, path::{Path, PathBuf}, str, @@ -92,6 +92,7 @@ pub struct Diagnostic { pub severity: DiagnosticSeverity, pub message: String, pub group_id: usize, + pub is_valid: bool, pub is_primary: bool, } @@ -725,7 +726,7 @@ impl Buffer { mut diagnostics: Vec>, cx: &mut ModelContext, ) -> Result { - diagnostics.sort_unstable_by_key(|d| (d.range.start, d.range.end)); + diagnostics.sort_unstable_by_key(|d| (d.range.start, Reverse(d.range.end))); let version = version.map(|version| version as usize); let content = if let Some(version) = version { @@ -754,6 +755,7 @@ impl Buffer { .peekable(); let mut last_edit_old_end = PointUtf16::zero(); let mut last_edit_new_end = PointUtf16::zero(); + let mut has_disk_based_diagnostics = false; let mut ix = 0; 'outer: while ix < diagnostics.len() { let entry = &mut diagnostics[ix]; @@ -769,6 +771,7 @@ impl Buffer { .as_ref() .map_or(false, |source| disk_based_sources.contains(source)) { + has_disk_based_diagnostics = true; while let Some(edit) = edits_since_save.peek() { if edit.old.end <= start { last_edit_old_end = edit.old.end; @@ -802,7 +805,62 @@ impl Buffer { } drop(edits_since_save); - self.diagnostics = DiagnosticSet::new(diagnostics, content); + + let diagnostics = diagnostics.into_iter().map(|entry| DiagnosticEntry { + range: content.anchor_before(entry.range.start)..content.anchor_after(entry.range.end), + diagnostic: entry.diagnostic, + }); + + // Some diagnostic sources are reported on a less frequent basis than others. + // If those sources are absent from this message, then preserve the previous + // diagnostics for those sources, but mark them as stale, and set a time to + // clear them out. + let mut merged_old_disk_based_diagnostics = false; + self.diagnostics = if has_disk_based_diagnostics { + DiagnosticSet::from_sorted_entries(diagnostics, content) + } else { + let mut new_diagnostics = diagnostics.peekable(); + let mut old_diagnostics = self + .diagnostics + .iter() + .filter_map(|entry| { + let is_disk_based = entry + .diagnostic + .source + .as_ref() + .map_or(false, |source| disk_based_sources.contains(source)); + if is_disk_based { + merged_old_disk_based_diagnostics = true; + let mut entry = entry.clone(); + entry.diagnostic.is_valid = false; + Some(entry) + } else { + None + } + }) + .peekable(); + let merged_diagnostics = + iter::from_fn(|| match (old_diagnostics.peek(), new_diagnostics.peek()) { + (None, None) => None, + (Some(_), None) => old_diagnostics.next(), + (None, Some(_)) => new_diagnostics.next(), + (Some(old), Some(new)) => { + let ordering = old + .range + .start + .cmp(&new.range.start, content) + .unwrap() + .then_with(|| new.range.end.cmp(&old.range.end, content).unwrap()); + if ordering.is_lt() { + old_diagnostics.next() + } else { + new_diagnostics.next() + } + } + }); + DiagnosticSet::from_sorted_entries(merged_diagnostics, content) + }; + self.diagnostics_update_count += 1; cx.notify(); cx.emit(Event::DiagnosticsUpdated); @@ -2009,6 +2067,7 @@ impl Default for Diagnostic { message: Default::default(), group_id: Default::default(), is_primary: Default::default(), + is_valid: Default::default(), } } } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 304a296088d14725b3c09e2cd851a84924cb3c4a..06d3609d59f9d787df2377d20c0e4a9ed84d3cb0 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -117,6 +117,7 @@ pub fn serialize_diagnostics<'a>( } as i32, group_id: entry.diagnostic.group_id as u64, is_primary: entry.diagnostic.is_primary, + is_valid: entry.diagnostic.is_valid, code: entry.diagnostic.code.clone(), source: entry.diagnostic.source.clone(), }) @@ -273,6 +274,7 @@ pub fn deserialize_diagnostics( is_primary: diagnostic.is_primary, code: diagnostic.code, source: diagnostic.source, + is_valid: diagnostic.is_valid, }, }) }) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 62c05adef55d42bfbc5067c196e10a6ef8c43cbd..53ea5f1bf84cc660b3c001e54bdc56d066fde00b 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -721,6 +721,7 @@ impl Worktree { message: mem::take(&mut diagnostic.message), group_id, is_primary: false, + is_valid: true, }, }); } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index d8fa9bc8e5c6c02df499ab81c26aacfd5450942c..3a36868b8d5b20a8caf306b64311f1cb876f97cd 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -303,10 +303,12 @@ message Diagnostic { Anchor end = 2; Severity severity = 3; string message = 4; - uint64 group_id = 5; - bool is_primary = 6; - optional string code = 7; - optional string source = 8; + optional string code = 5; + optional string source = 6; + uint64 group_id = 7; + bool is_primary = 8; + bool is_valid = 9; + enum Severity { None = 0; Error = 1;