Add a unit test for preserving disk-based diagnostics

Max Brunsfeld created

Change summary

crates/language/src/buffer.rs |   2 
crates/language/src/tests.rs  | 101 +++++++++++++++++++++++++++++++++++++
crates/text/src/text.rs       |  10 ++
3 files changed, 110 insertions(+), 3 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -2067,7 +2067,7 @@ impl Default for Diagnostic {
             message: Default::default(),
             group_id: Default::default(),
             is_primary: Default::default(),
-            is_valid: Default::default(),
+            is_valid: true,
         }
     }
 }

crates/language/src/tests.rs 🔗

@@ -750,6 +750,107 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) {
     });
 }
 
+#[gpui::test]
+async fn test_preserving_disk_based_diagnostics(mut cx: gpui::TestAppContext) {
+    let buffer = cx.add_model(|cx| {
+        let text = "
+            use a::*;
+            const b: i32 = c::;
+            const c: i32 = d;
+            const e: i32 = f +;
+        "
+        .unindent();
+
+        let mut rust_lang = rust_lang();
+        rust_lang.config.language_server = Some(LanguageServerConfig {
+            disk_based_diagnostic_sources: HashSet::from_iter(["disk".to_string()]),
+            ..Default::default()
+        });
+
+        let mut buffer = Buffer::new(0, text, cx);
+        buffer.set_language(Some(Arc::new(rust_lang)), None, cx);
+        buffer
+    });
+
+    // Initially, there are three errors. The second one is disk-based.
+    let diagnostics = vec![
+        DiagnosticEntry {
+            range: PointUtf16::new(1, 16)..PointUtf16::new(1, 18),
+            diagnostic: Diagnostic {
+                severity: DiagnosticSeverity::ERROR,
+                message: "syntax error 1".to_string(),
+                group_id: 0,
+                is_primary: true,
+                is_valid: true,
+                ..Default::default()
+            },
+        },
+        DiagnosticEntry {
+            range: PointUtf16::new(2, 15)..PointUtf16::new(2, 16),
+            diagnostic: Diagnostic {
+                severity: DiagnosticSeverity::ERROR,
+                message: "cannot find value `d` in this scope".to_string(),
+                source: Some("disk".to_string()),
+                group_id: 1,
+                is_primary: true,
+                is_valid: true,
+                ..Default::default()
+            },
+        },
+        DiagnosticEntry {
+            range: PointUtf16::new(3, 17)..PointUtf16::new(3, 18),
+            diagnostic: Diagnostic {
+                severity: DiagnosticSeverity::ERROR,
+                message: "syntax error 2".to_string(),
+                group_id: 2,
+                is_primary: true,
+                is_valid: true,
+                ..Default::default()
+            },
+        },
+    ];
+    buffer.update(&mut cx, |buffer, cx| {
+        buffer
+            .update_diagnostics(None, diagnostics.clone(), cx)
+            .unwrap();
+        assert_eq!(
+            buffer
+                .snapshot()
+                .diagnostics_in_range::<_, PointUtf16>(PointUtf16::new(0, 0)..PointUtf16::new(4, 0))
+                .collect::<Vec<_>>(),
+            diagnostics.as_slice(),
+        );
+    });
+
+    // The diagnostics are updated, and the disk-based diagnostic is omitted from this message.
+    let mut new_diagnostics = vec![diagnostics[0].clone(), diagnostics[2].clone()];
+    new_diagnostics[0].diagnostic.message = "another syntax error".to_string();
+    new_diagnostics[1].diagnostic.message = "yet another syntax error".to_string();
+
+    buffer.update(&mut cx, |buffer, cx| {
+        buffer
+            .update_diagnostics(None, new_diagnostics.clone(), cx)
+            .unwrap();
+        assert_eq!(
+            buffer
+                .snapshot()
+                .diagnostics_in_range::<_, PointUtf16>(PointUtf16::new(0, 0)..PointUtf16::new(4, 0))
+                .collect::<Vec<_>>(),
+            &[
+                new_diagnostics[0].clone(),
+                DiagnosticEntry {
+                    range: diagnostics[1].range.clone(),
+                    diagnostic: Diagnostic {
+                        is_valid: false,
+                        ..diagnostics[1].diagnostic.clone()
+                    },
+                },
+                new_diagnostics[1].clone(),
+            ],
+        );
+    });
+}
+
 #[gpui::test]
 async fn test_empty_diagnostic_ranges(mut cx: gpui::TestAppContext) {
     cx.add_model(|cx| {

crates/text/src/text.rs 🔗

@@ -2057,12 +2057,18 @@ pub trait FromAnchor {
 
 impl FromAnchor for Point {
     fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self {
-        anchor.to_point(snapshot)
+        snapshot.summary_for_anchor(anchor)
+    }
+}
+
+impl FromAnchor for PointUtf16 {
+    fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self {
+        snapshot.summary_for_anchor(anchor)
     }
 }
 
 impl FromAnchor for usize {
     fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self {
-        anchor.to_offset(snapshot)
+        snapshot.summary_for_anchor(anchor)
     }
 }