Fix #24081 - lsp diagnostic code type conversion (#24347)

Ben Kunkle created

- **store `buffer::Diagnostic`as NumberOrString instead of assuming
String**
- **update zed-industries/lsp-types rev**

Closes #24081

Release Notes:

- Fixed an issue where language server diagnostic codes would be converted to strings leading to errors with some language servers

Change summary

crates/diagnostics/src/diagnostics.rs |  2 +-
crates/language/src/buffer.rs         |  4 ++--
crates/language/src/diagnostic_set.rs |  8 +-------
crates/language/src/proto.rs          |  4 ++--
crates/lsp/Cargo.toml                 |  2 +-
crates/project/src/lsp_store.rs       | 14 +++++---------
6 files changed, 12 insertions(+), 22 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -933,7 +933,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
                             .when_some(diagnostic.code.as_ref(), |stack, code| {
                                 stack.child(
                                     div()
-                                        .child(SharedString::from(format!("({code})")))
+                                        .child(SharedString::from(format!("({code:?})")))
                                         .text_color(color.text_muted),
                                 )
                             }),

crates/language/src/buffer.rs 🔗

@@ -28,7 +28,7 @@ use gpui::{
     AnyElement, App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, Pixels,
     SharedString, StyledText, Task, TaskLabel, TextStyle, Window,
 };
-use lsp::LanguageServerId;
+use lsp::{LanguageServerId, NumberOrString};
 use parking_lot::Mutex;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -202,7 +202,7 @@ pub struct Diagnostic {
     /// The name of the service that produced this diagnostic.
     pub source: Option<String>,
     /// A machine-readable code that identifies this diagnostic.
-    pub code: Option<String>,
+    pub code: Option<NumberOrString>,
     /// Whether this diagnostic is a hint, warning, or error.
     pub severity: DiagnosticSeverity,
     /// The human-readable message associated with this diagnostic.

crates/language/src/diagnostic_set.rs 🔗

@@ -56,17 +56,11 @@ impl DiagnosticEntry<PointUtf16> {
     /// Returns a raw LSP diagnostic used to provide diagnostic context to LSP
     /// codeAction request
     pub fn to_lsp_diagnostic_stub(&self) -> Result<lsp::Diagnostic> {
-        let code = self
-            .diagnostic
-            .code
-            .clone()
-            .map(lsp::NumberOrString::String);
-
         let range = range_to_lsp(self.range.clone())?;
 
         Ok(lsp::Diagnostic {
-            code,
             range,
+            code: self.diagnostic.code.clone(),
             severity: Some(self.diagnostic.severity),
             source: self.diagnostic.source.clone(),
             message: self.diagnostic.message.clone(),

crates/language/src/proto.rs 🔗

@@ -213,7 +213,7 @@ pub fn serialize_diagnostics<'a>(
             group_id: entry.diagnostic.group_id as u64,
             is_primary: entry.diagnostic.is_primary,
             is_valid: true,
-            code: entry.diagnostic.code.clone(),
+            code: entry.diagnostic.code.as_ref().map(|s| s.to_string()),
             is_disk_based: entry.diagnostic.is_disk_based,
             is_unnecessary: entry.diagnostic.is_unnecessary,
             data: entry.diagnostic.data.as_ref().map(|data| data.to_string()),
@@ -419,7 +419,7 @@ pub fn deserialize_diagnostics(
                     },
                     message: diagnostic.message,
                     group_id: diagnostic.group_id as usize,
-                    code: diagnostic.code,
+                    code: diagnostic.code.map(lsp::NumberOrString::from_string),
                     is_primary: diagnostic.is_primary,
                     is_disk_based: diagnostic.is_disk_based,
                     is_unnecessary: diagnostic.is_unnecessary,

crates/lsp/Cargo.toml 🔗

@@ -22,7 +22,7 @@ collections.workspace = true
 futures.workspace = true
 gpui.workspace = true
 log.workspace = true
-lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "72357d6f6d212bdffba3b5ef4b31d8ca856058e7" }
+lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "1fff0dd12e2071c5667327394cfec163d2a466ab" }
 parking_lot.workspace = true
 postage.workspace = true
 serde.workspace = true

crates/project/src/lsp_store.rs 🔗

@@ -7366,10 +7366,6 @@ impl LspStore {
 
         for diagnostic in &params.diagnostics {
             let source = diagnostic.source.as_ref();
-            let code = diagnostic.code.as_ref().map(|code| match code {
-                lsp::NumberOrString::Number(code) => code.to_string(),
-                lsp::NumberOrString::String(code) => code.clone(),
-            });
             let range = range_from_lsp(diagnostic.range);
             let is_supporting = diagnostic
                 .related_information
@@ -7378,7 +7374,7 @@ impl LspStore {
                     infos.iter().any(|info| {
                         primary_diagnostic_group_ids.contains_key(&(
                             source,
-                            code.clone(),
+                            diagnostic.code.clone(),
                             range_from_lsp(info.location.range),
                         ))
                     })
@@ -7390,7 +7386,7 @@ impl LspStore {
 
             if is_supporting {
                 supporting_diagnostics.insert(
-                    (source, code.clone(), range),
+                    (source, diagnostic.code.clone(), range),
                     (diagnostic.severity, is_unnecessary),
                 );
             } else {
@@ -7400,13 +7396,13 @@ impl LspStore {
 
                 sources_by_group_id.insert(group_id, source);
                 primary_diagnostic_group_ids
-                    .insert((source, code.clone(), range.clone()), group_id);
+                    .insert((source, diagnostic.code.clone(), range.clone()), group_id);
 
                 diagnostics.push(DiagnosticEntry {
                     range,
                     diagnostic: Diagnostic {
                         source: diagnostic.source.clone(),
-                        code: code.clone(),
+                        code: diagnostic.code.clone(),
                         severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
                         message: diagnostic.message.trim().to_string(),
                         group_id,
@@ -7424,7 +7420,7 @@ impl LspStore {
                                 range,
                                 diagnostic: Diagnostic {
                                     source: diagnostic.source.clone(),
-                                    code: code.clone(),
+                                    code: diagnostic.code.clone(),
                                     severity: DiagnosticSeverity::INFORMATION,
                                     message: info.message.trim().to_string(),
                                     group_id,