Emit an `UpdateDiagnostics` from Worktree when buffer diagnostics change

Antonio Scandurra created

Change summary

crates/language/src/buffer.rs  | 13 ++++---------
crates/project/src/project.rs  |  2 +-
crates/project/src/worktree.rs | 30 +++++++++++++++++++++++++++++-
3 files changed, 34 insertions(+), 11 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -725,6 +725,10 @@ impl Buffer {
         cx.notify();
     }
 
+    pub fn all_diagnostics<'a>(&'a self) -> impl 'a + Iterator<Item = &'a DiagnosticEntry<Anchor>> {
+        self.diagnostics.iter()
+    }
+
     pub fn update_diagnostics(
         &mut self,
         version: Option<i32>,
@@ -1859,15 +1863,6 @@ impl BufferSnapshot {
             })
     }
 
-    pub fn all_diagnostics<'a, O>(&'a self) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
-    where
-        O: 'a + FromAnchor,
-    {
-        self.diagnostics
-            .iter()
-            .map(|diagnostic| diagnostic.resolve(self))
-    }
-
     pub fn diagnostics_in_range<'a, T, O>(
         &'a self,
         search_range: Range<T>,

crates/project/src/project.rs 🔗

@@ -78,7 +78,7 @@ pub struct DiagnosticSummary {
 }
 
 impl DiagnosticSummary {
-    fn new<T>(diagnostics: &[DiagnosticEntry<T>]) -> Self {
+    fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
         let mut this = Self {
             error_count: 0,
             warning_count: 0,

crates/project/src/worktree.rs 🔗

@@ -1097,15 +1097,43 @@ impl LocalWorktree {
                 buffer
             });
 
-            this.update(&mut cx, |this, _| {
+            this.update(&mut cx, |this, cx| {
                 let this = this.as_local_mut().unwrap();
                 this.open_buffers.insert(buffer.id(), buffer.downgrade());
+                cx.subscribe(&buffer, |worktree, buffer, event, cx| {
+                    worktree
+                        .as_local_mut()
+                        .unwrap()
+                        .on_buffer_event(buffer, event, cx);
+                })
+                .detach();
             });
 
             Ok(buffer)
         })
     }
 
+    fn on_buffer_event(
+        &mut self,
+        buffer: ModelHandle<Buffer>,
+        event: &language::Event,
+        cx: &mut ModelContext<Worktree>,
+    ) {
+        match event {
+            language::Event::DiagnosticsUpdated => {
+                let buffer = buffer.read(cx);
+                if let Some(path) = buffer.file().map(|file| file.path().clone()) {
+                    let diagnostics = buffer.all_diagnostics();
+                    self.diagnostic_summaries
+                        .insert(path.clone(), DiagnosticSummary::new(diagnostics));
+                    cx.emit(Event::DiagnosticsUpdated(path));
+                    cx.notify();
+                }
+            }
+            _ => {}
+        }
+    }
+
     pub fn open_remote_buffer(
         &mut self,
         envelope: TypedEnvelope<proto::OpenBuffer>,