Allow querying a diagnostic group by its id

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/buffer/src/anchor.rs  | 32 +++++++++++++++++++++++
crates/language/src/lib.rs   | 13 +++++++++
crates/language/src/tests.rs | 51 ++++++++++++++++++++++++++++++++++++++
3 files changed, 96 insertions(+)

Detailed changes

crates/buffer/src/anchor.rs 🔗

@@ -354,6 +354,38 @@ impl<T: Clone> AnchorRangeMultimap<T> {
             .cursor::<()>()
             .map(|entry| (entry.range.start..entry.range.end, &entry.value))
     }
+
+    pub fn filter<'a, O, F>(
+        &'a self,
+        content: Content<'a>,
+        mut f: F,
+    ) -> impl 'a + Iterator<Item = (usize, Range<O>, &T)>
+    where
+        O: FromAnchor,
+        F: 'a + FnMut(&'a T) -> bool,
+    {
+        let mut endpoint = Anchor {
+            full_offset: FullOffset(0),
+            bias: Bias::Left,
+            version: self.version.clone(),
+        };
+        self.entries
+            .cursor::<()>()
+            .enumerate()
+            .filter_map(move |(ix, entry)| {
+                if f(&entry.value) {
+                    endpoint.full_offset = entry.range.start;
+                    endpoint.bias = self.start_bias;
+                    let start = O::from_anchor(&endpoint, &content);
+                    endpoint.full_offset = entry.range.end;
+                    endpoint.bias = self.end_bias;
+                    let end = O::from_anchor(&endpoint, &content);
+                    Some((ix, start..end, &entry.value))
+                } else {
+                    None
+                }
+            })
+    }
 }
 
 impl<T: Clone> sum_tree::Item for AnchorRangeMultimapEntry<T> {

crates/language/src/lib.rs 🔗

@@ -812,6 +812,19 @@ impl Buffer {
             .map(move |(_, range, diagnostic)| (range, diagnostic))
     }
 
+    pub fn diagnostic_group<'a, O>(
+        &'a self,
+        group_id: usize,
+    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
+    where
+        O: 'a + FromAnchor,
+    {
+        let content = self.content();
+        self.diagnostics
+            .filter(content, move |diagnostic| diagnostic.group_id == group_id)
+            .map(move |(_, range, diagnostic)| (range, diagnostic))
+    }
+
     pub fn diagnostics_update_count(&self) -> usize {
         self.diagnostics_update_count
     }

crates/language/src/tests.rs 🔗

@@ -846,6 +846,57 @@ async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
             ]
         );
 
+        assert_eq!(
+            buffer.diagnostic_group(0).collect::<Vec<_>>(),
+            &[
+                (
+                    Point::new(1, 8)..Point::new(1, 9),
+                    &Diagnostic {
+                        severity: DiagnosticSeverity::WARNING,
+                        message: "error 1".to_string(),
+                        group_id: 0
+                    }
+                ),
+                (
+                    Point::new(1, 8)..Point::new(1, 9),
+                    &Diagnostic {
+                        severity: DiagnosticSeverity::HINT,
+                        message: "error 1 hint 1".to_string(),
+                        group_id: 0
+                    }
+                ),
+            ]
+        );
+        assert_eq!(
+            buffer.diagnostic_group(1).collect::<Vec<_>>(),
+            &[
+                (
+                    Point::new(1, 13)..Point::new(1, 15),
+                    &Diagnostic {
+                        severity: DiagnosticSeverity::HINT,
+                        message: "error 2 hint 1".to_string(),
+                        group_id: 1
+                    }
+                ),
+                (
+                    Point::new(1, 13)..Point::new(1, 15),
+                    &Diagnostic {
+                        severity: DiagnosticSeverity::HINT,
+                        message: "error 2 hint 2".to_string(),
+                        group_id: 1
+                    }
+                ),
+                (
+                    Point::new(2, 8)..Point::new(2, 17),
+                    &Diagnostic {
+                        severity: DiagnosticSeverity::ERROR,
+                        message: "error 2".to_string(),
+                        group_id: 1
+                    }
+                )
+            ]
+        );
+
         buffer
     });
 }