diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index d24b9f703375edc19e853091eb55c34956618455..90d619480119dfb1c504e462a4b92a5bb7416e63 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -13,6 +13,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use clock::ReplicaId; +use collections::HashMap; use fs::LineEnding; use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, Task}; @@ -71,7 +72,7 @@ pub struct Buffer { syntax_map: Mutex, parsing_in_background: bool, parse_count: usize, - diagnostics: DiagnosticSet, + diagnostics: HashMap, // server_id -> diagnostic set remote_selections: TreeMap, selections_update_count: usize, diagnostics_update_count: usize, @@ -88,7 +89,7 @@ pub struct BufferSnapshot { pub git_diff: git::diff::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, - diagnostics: DiagnosticSet, + diagnostics: HashMap, // server_id -> diagnostic set diagnostics_update_count: usize, file_update_count: usize, git_diff_update_count: usize, @@ -164,16 +165,20 @@ pub struct CodeAction { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Operation { Buffer(text::Operation), + UpdateDiagnostics { + server_id: usize, diagnostics: Arc<[DiagnosticEntry]>, lamport_timestamp: clock::Lamport, }, + UpdateSelections { selections: Arc<[Selection]>, lamport_timestamp: clock::Lamport, line_mode: bool, cursor_shape: CursorShape, }, + UpdateCompletionTriggers { triggers: Vec, lamport_timestamp: clock::Lamport, @@ -409,6 +414,7 @@ impl Buffer { ) -> Task> { let mut operations = Vec::new(); operations.extend(self.deferred_ops.iter().map(proto::serialize_operation)); + operations.extend(self.remote_selections.iter().map(|(_, set)| { proto::serialize_operation(&Operation::UpdateSelections { selections: set.selections.clone(), @@ -417,10 +423,15 @@ impl Buffer { cursor_shape: set.cursor_shape, }) })); - operations.push(proto::serialize_operation(&Operation::UpdateDiagnostics { - diagnostics: self.diagnostics.iter().cloned().collect(), - lamport_timestamp: self.diagnostics_timestamp, - })); + + for (server_id, diagnostics) in &self.diagnostics { + operations.push(proto::serialize_operation(&Operation::UpdateDiagnostics { + lamport_timestamp: self.diagnostics_timestamp, + server_id: *server_id, + diagnostics: diagnostics.iter().cloned().collect(), + })); + } + operations.push(proto::serialize_operation( &Operation::UpdateCompletionTriggers { triggers: self.completion_triggers.clone(), @@ -866,13 +877,19 @@ impl Buffer { cx.notify(); } - pub fn update_diagnostics(&mut self, diagnostics: DiagnosticSet, cx: &mut ModelContext) { + pub fn update_diagnostics( + &mut self, + server_id: usize, + diagnostics: DiagnosticSet, + cx: &mut ModelContext, + ) { let lamport_timestamp = self.text.lamport_clock.tick(); let op = Operation::UpdateDiagnostics { + server_id, diagnostics: diagnostics.iter().cloned().collect(), lamport_timestamp, }; - self.apply_diagnostic_update(diagnostics, lamport_timestamp, cx); + self.apply_diagnostic_update(server_id, diagnostics, lamport_timestamp, cx); self.send_operation(op, cx); } @@ -1580,11 +1597,13 @@ impl Buffer { unreachable!("buffer operations should never be applied at this layer") } Operation::UpdateDiagnostics { + server_id, diagnostics: diagnostic_set, lamport_timestamp, } => { let snapshot = self.snapshot(); self.apply_diagnostic_update( + server_id, DiagnosticSet::from_sorted_entries(diagnostic_set.iter().cloned(), &snapshot), lamport_timestamp, cx, @@ -1626,12 +1645,13 @@ impl Buffer { fn apply_diagnostic_update( &mut self, + server_id: usize, diagnostics: DiagnosticSet, lamport_timestamp: clock::Lamport, cx: &mut ModelContext, ) { if lamport_timestamp > self.diagnostics_timestamp { - self.diagnostics = diagnostics; + self.diagnostics.insert(server_id, diagnostics); self.diagnostics_timestamp = lamport_timestamp; self.diagnostics_update_count += 1; self.text.lamport_clock.observe(lamport_timestamp); @@ -2505,14 +2525,40 @@ impl BufferSnapshot { ) -> impl 'a + Iterator> where T: 'a + Clone + ToOffset, - O: 'a + FromAnchor, + O: 'a + FromAnchor + Ord, { - self.diagnostics.range(search_range, self, true, reversed) + let mut iterators: Vec<_> = self + .diagnostics + .values() + .map(|collection| { + collection + .range::(search_range.clone(), self, true, reversed) + .peekable() + }) + .collect(); + + std::iter::from_fn(move || { + let (next_ix, _) = iterators + .iter_mut() + .enumerate() + .flat_map(|(ix, iter)| Some((ix, iter.peek()?))) + .min_by(|(_, a), (_, b)| a.range.start.cmp(&b.range.start))?; + iterators[next_ix].next() + }) } pub fn diagnostic_groups(&self) -> Vec> { let mut groups = Vec::new(); - self.diagnostics.groups(&mut groups, self); + for diagnostics in self.diagnostics.values() { + diagnostics.groups(&mut groups, self); + } + + groups.sort_by(|a, b| { + let a_start = &a.entries[a.primary_ix].range.start; + let b_start = &b.entries[b.primary_ix].range.start; + a_start.cmp(b_start, self) + }); + groups } @@ -2523,7 +2569,9 @@ impl BufferSnapshot { where O: 'a + FromAnchor, { - self.diagnostics.group(group_id, self) + self.diagnostics + .values() + .flat_map(move |set| set.group(group_id, self)) } pub fn diagnostics_update_count(&self) -> usize { diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 4675e4e9dcf16ec097b741494d9590beebabd38e..6b6ce041f700e0a81ac12bfb26fea17520bf06ff 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1866,7 +1866,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { buffer, ); log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics); - buffer.update_diagnostics(diagnostics, cx); + buffer.update_diagnostics(0, diagnostics, cx); }); mutation_count -= 1; } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index fb50f2a7432a45818cff0836ee8522f62a0328bd..e4963e0882a4f982218382ae20d2a50f09d2ca09 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -40,6 +40,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation { crate::Operation::Buffer(text::Operation::Edit(edit)) => { proto::operation::Variant::Edit(serialize_edit_operation(edit)) } + crate::Operation::Buffer(text::Operation::Undo { undo, lamport_timestamp, @@ -58,6 +59,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation { }) .collect(), }), + crate::Operation::UpdateSelections { selections, line_mode, @@ -70,14 +72,18 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation { line_mode: *line_mode, cursor_shape: serialize_cursor_shape(cursor_shape) as i32, }), + crate::Operation::UpdateDiagnostics { - diagnostics, lamport_timestamp, + server_id, + diagnostics, } => proto::operation::Variant::UpdateDiagnostics(proto::UpdateDiagnostics { replica_id: lamport_timestamp.replica_id as u32, lamport_timestamp: lamport_timestamp.value, + server_id: *server_id as u64, diagnostics: serialize_diagnostics(diagnostics.iter()), }), + crate::Operation::UpdateCompletionTriggers { triggers, lamport_timestamp, @@ -267,11 +273,12 @@ pub fn deserialize_operation(message: proto::Operation) -> Result { crate::Operation::UpdateDiagnostics { - diagnostics: deserialize_diagnostics(message.diagnostics), lamport_timestamp: clock::Lamport { replica_id: message.replica_id as ReplicaId, value: message.lamport_timestamp, }, + server_id: message.server_id as usize, + diagnostics: deserialize_diagnostics(message.diagnostics), } } proto::operation::Variant::UpdateCompletionTriggers(message) => { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 536d803fea5c70925e82d1077e7085f6b638f2a3..6ace10f6df05c27965e5ade4e5634eb7263027d2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1675,7 +1675,7 @@ impl Project { if let Some(local_worktree) = file.worktree.read(cx).as_local() { for (server_id, diagnostics) in local_worktree.diagnostics_for_path(file.path()) { - self.update_buffer_diagnostics(buffer_handle, diagnostics, server_id, None, cx) + self.update_buffer_diagnostics(buffer_handle, server_id, None, diagnostics, cx) .log_err(); } } @@ -2968,7 +2968,7 @@ impl Project { }; if let Some(buffer) = self.get_open_buffer(&project_path, cx) { - self.update_buffer_diagnostics(&buffer, diagnostics.clone(), server_id, version, cx)?; + self.update_buffer_diagnostics(&buffer, server_id, version, diagnostics.clone(), cx)?; } let updated = worktree.update(cx, |worktree, cx| { @@ -2989,9 +2989,9 @@ impl Project { fn update_buffer_diagnostics( &mut self, buffer: &ModelHandle, - mut diagnostics: Vec>>, server_id: usize, version: Option, + mut diagnostics: Vec>>, cx: &mut ModelContext, ) -> Result<()> { fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering { @@ -3053,7 +3053,9 @@ impl Project { drop(edits_since_save); let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot); - buffer.update(cx, |buffer, cx| buffer.update_diagnostics(set, cx)); + buffer.update(cx, |buffer, cx| { + buffer.update_diagnostics(server_id, set, cx) + }); Ok(()) } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 72b66f3d78dee251429ae91246723d8e91106bf5..d8272f21f2bf22c4eefb389e9c5f2f061f9b9e8d 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -861,7 +861,8 @@ message IncomingContactRequest { message UpdateDiagnostics { uint32 replica_id = 1; uint32 lamport_timestamp = 2; - repeated Diagnostic diagnostics = 3; + uint64 server_id = 3; + repeated Diagnostic diagnostics = 4; } message Follow {