changeset 6663:1498ac846a0b

Add pull diagnostics `identifier` to LSP diagnostic provider This includes a change to lsp-types to store the identifier as an Arc since it will be cloned for each diagnostic.
author Michael Davis <mcarsondavis@gmail.com>
date Fri, 21 Mar 2025 10:05:50 -0400
parents 01eea256899c
children 09ef7fe92427
files helix-core/src/diagnostic.rs helix-lsp-types/src/document_diagnostic.rs helix-term/src/application.rs
diffstat 3 files changed, 49 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/helix-core/src/diagnostic.rs	Fri Mar 21 09:44:40 2025 -0400
+++ b/helix-core/src/diagnostic.rs	Fri Mar 21 10:05:50 2025 -0400
@@ -1,5 +1,5 @@
 //! LSP diagnostic utility types.
-use std::fmt;
+use std::{fmt, sync::Arc};
 
 pub use helix_stdx::range::Range;
 use serde::{Deserialize, Serialize};
@@ -50,9 +50,24 @@
     pub data: Option<serde_json::Value>,
 }
 
+/// The source of a diagnostic.
+///
+/// This type is cheap to clone: all data is either `Copy` or wrapped in an `Arc`.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub enum DiagnosticProvider {
-    Lsp { server_id: LanguageServerId },
+    Lsp {
+        /// The ID of the language server which sent the diagnostic.
+        server_id: LanguageServerId,
+        /// An optional identifier under which diagnostics are managed by the client.
+        ///
+        /// `identifier` is a field from the LSP "Pull Diagnostics" feature meant to provide an
+        /// optional "namespace" for diagnostics: a language server can respond to a diagnostics
+        /// pull request with an identifier and these diagnostics should be treated as separate
+        /// from push diagnostics. Rust-analyzer uses this feature for example to provide Cargo
+        /// diagnostics with push and internal diagnostics with pull. The push diagnostics should
+        /// not clear the pull diagnostics and vice-versa.
+        identifier: Option<Arc<str>>,
+    },
     // Future internal features can go here...
 }
 
--- a/helix-lsp-types/src/document_diagnostic.rs	Fri Mar 21 09:44:40 2025 -0400
+++ b/helix-lsp-types/src/document_diagnostic.rs	Fri Mar 21 10:05:50 2025 -0400
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use serde::{Deserialize, Serialize};
 
@@ -33,8 +33,13 @@
 pub struct DiagnosticOptions {
     /// An optional identifier under which the diagnostics are
     /// managed by the client.
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub identifier: Option<String>,
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        serialize_with = "serialize_option_arc_str",
+        deserialize_with = "deserialize_option_arc_str"
+    )]
+    pub identifier: Option<Arc<str>>,
 
     /// Whether the language has inter file dependencies, meaning that editing code in one file can
     /// result in a different diagnostic set in another file. Inter file dependencies are common
@@ -48,6 +53,19 @@
     pub work_done_progress_options: WorkDoneProgressOptions,
 }
 
+fn serialize_option_arc_str<S: serde::Serializer>(
+    val: &Option<Arc<str>>,
+    serializer: S,
+) -> Result<S::Ok, S::Error> {
+    serializer.serialize_str(val.as_ref().unwrap())
+}
+
+fn deserialize_option_arc_str<'de, D: serde::Deserializer<'de>>(
+    deserializer: D,
+) -> Result<Option<Arc<str>>, D::Error> {
+    Option::<String>::deserialize(deserializer).map(|opt| opt.map(|s| s.into()))
+}
+
 /// Diagnostic registration options.
 ///
 /// @since 3.17.0
@@ -81,7 +99,13 @@
     pub text_document: TextDocumentIdentifier,
 
     /// The additional identifier provided during registration.
-    pub identifier: Option<String>,
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        serialize_with = "serialize_option_arc_str",
+        deserialize_with = "deserialize_option_arc_str"
+    )]
+    pub identifier: Option<Arc<str>>,
 
     /// The result ID of a previous response if provided.
     pub previous_result_id: Option<String>,
--- a/helix-term/src/application.rs	Fri Mar 21 09:44:40 2025 -0400
+++ b/helix-term/src/application.rs	Fri Mar 21 10:05:50 2025 -0400
@@ -740,8 +740,10 @@
                             log::error!("Discarding publishDiagnostic notification sent by an uninitialized server: {}", language_server.name());
                             return;
                         }
-                        let provider =
-                            helix_core::diagnostic::DiagnosticProvider::Lsp { server_id };
+                        let provider = helix_core::diagnostic::DiagnosticProvider::Lsp {
+                            server_id,
+                            identifier: None,
+                        };
                         self.editor.handle_lsp_diagnostics(
                             &provider,
                             uri,