Fix bug in `to_full_offset` when `Anchor::version` != `Content::version`

Antonio Scandurra created

Change summary

crates/buffer/src/lib.rs     |  4 -
crates/language/src/tests.rs | 96 ++++++++++++++++++++++++++++++++++++-
2 files changed, 92 insertions(+), 8 deletions(-)

Detailed changes

crates/buffer/src/lib.rs 🔗

@@ -2496,10 +2496,6 @@ impl ToOffset for Anchor {
     fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize {
         content.into().summary_for_anchor(self).bytes
     }
-
-    fn to_full_offset<'a>(&self, _: impl Into<Content<'a>>, _: Bias) -> usize {
-        self.full_offset
-    }
 }
 
 impl<'a> ToOffset for &'a Anchor {

crates/language/src/tests.rs 🔗

@@ -476,11 +476,99 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
                 }
             ]
         );
+        assert_eq!(
+            chunks_with_diagnostics(buffer, 0..buffer.len()),
+            [
+                ("\n\nfn a() { ".to_string(), None),
+                ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
+                (" }\nfn b() { ".to_string(), None),
+                ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
+                (" }\nfn c() { ".to_string(), None),
+                ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
+                (" }\n".to_string(), None),
+            ]
+        );
+        assert_eq!(
+            chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
+            [
+                ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
+                (" }\nfn c() { ".to_string(), None),
+                ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
+            ]
+        );
 
-        dbg!(buffer
-            .snapshot()
-            .highlighted_text_for_range(0..buffer.len())
-            .collect::<Vec<_>>());
+        // Ensure overlapping diagnostics are highlighted correctly.
+        buffer
+            .update_diagnostics(
+                Some(open_notification.text_document.version),
+                vec![
+                    lsp::Diagnostic {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
+                        severity: Some(lsp::DiagnosticSeverity::ERROR),
+                        message: "undefined variable 'A'".to_string(),
+                        ..Default::default()
+                    },
+                    lsp::Diagnostic {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
+                        severity: Some(lsp::DiagnosticSeverity::WARNING),
+                        message: "unreachable statement".to_string(),
+                        ..Default::default()
+                    },
+                ],
+                cx,
+            )
+            .unwrap();
+        assert_eq!(
+            buffer
+                .diagnostics_in_range(Point::new(2, 0)..Point::new(3, 0))
+                .collect::<Vec<_>>(),
+            &[
+                Diagnostic {
+                    range: Point::new(2, 9)..Point::new(2, 12),
+                    severity: DiagnosticSeverity::WARNING,
+                    message: "unreachable statement".to_string()
+                },
+                Diagnostic {
+                    range: Point::new(2, 9)..Point::new(2, 10),
+                    severity: DiagnosticSeverity::ERROR,
+                    message: "undefined variable 'A'".to_string()
+                },
+            ]
+        );
+        assert_eq!(
+            chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
+            [
+                ("fn a() { ".to_string(), None),
+                ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
+                (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
+                ("\n".to_string(), None),
+            ]
+        );
+        assert_eq!(
+            chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
+            [
+                (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
+                ("\n".to_string(), None),
+            ]
+        );
+
+        fn chunks_with_diagnostics<T: ToOffset>(
+            buffer: &Buffer,
+            range: Range<T>,
+        ) -> Vec<(String, Option<DiagnosticSeverity>)> {
+            let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
+            for chunk in buffer.snapshot().highlighted_text_for_range(range) {
+                if chunks
+                    .last()
+                    .map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)
+                {
+                    chunks.last_mut().unwrap().0.push_str(chunk.text);
+                } else {
+                    chunks.push((chunk.text.to_string(), chunk.diagnostic));
+                }
+            }
+            chunks
+        }
     });
 }