lsp: Pass back diagnostic .data when querying code actions for it (#14962)

Per the LSP spec, we should pass .data field of diagnostics into code
action request:
```
	/**
	 * A data entry field that is preserved between a
	 * `textDocument/publishDiagnostics` notification and
	 * `textDocument/codeAction` request. *
	 * @since 3.16.0 */ data?: LSPAny;
```


Release Notes:

- Fixed rare cases where a code action triggered by diagnostic may not
be available for use.
This commit is contained in:
Piotr Osiewicz 2024-07-22 17:49:11 +02:00 committed by GitHub
parent 10d2353e07
commit 865904a0c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 18 additions and 1 deletions

View File

@ -954,6 +954,7 @@ fn random_diagnostic(
is_primary, is_primary,
is_disk_based: false, is_disk_based: false,
is_unnecessary: false, is_unnecessary: false,
data: None,
}, },
} }
} }

View File

@ -27,6 +27,7 @@ use gpui::{
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp::LanguageServerId; use lsp::LanguageServerId;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde_json::Value;
use similar::{ChangeTag, TextDiff}; use similar::{ChangeTag, TextDiff};
use smallvec::SmallVec; use smallvec::SmallVec;
use smol::future::yield_now; use smol::future::yield_now;
@ -213,6 +214,8 @@ pub struct Diagnostic {
pub is_disk_based: bool, pub is_disk_based: bool,
/// Whether this diagnostic marks unnecessary code. /// Whether this diagnostic marks unnecessary code.
pub is_unnecessary: bool, pub is_unnecessary: bool,
/// Data from language server that produced this diagnostic. Passed back to the LS when we request code actions for this diagnostic.
pub data: Option<Value>,
} }
/// TODO - move this into the `project` crate and make it private. /// TODO - move this into the `project` crate and make it private.
@ -3844,6 +3847,7 @@ impl Default for Diagnostic {
is_primary: false, is_primary: false,
is_disk_based: false, is_disk_based: false,
is_unnecessary: false, is_unnecessary: false,
data: None,
} }
} }
} }

View File

@ -69,6 +69,7 @@ impl DiagnosticEntry<PointUtf16> {
severity: Some(self.diagnostic.severity), severity: Some(self.diagnostic.severity),
source: self.diagnostic.source.clone(), source: self.diagnostic.source.clone(),
message: self.diagnostic.message.clone(), message: self.diagnostic.message.clone(),
data: self.diagnostic.data.clone(),
..Default::default() ..Default::default()
} }
} }

View File

@ -5,7 +5,8 @@ use anyhow::{anyhow, Context as _, Result};
use clock::ReplicaId; use clock::ReplicaId;
use lsp::{DiagnosticSeverity, LanguageServerId}; use lsp::{DiagnosticSeverity, LanguageServerId};
use rpc::proto; use rpc::proto;
use std::{ops::Range, sync::Arc}; use serde_json::Value;
use std::{ops::Range, str::FromStr, sync::Arc};
use text::*; use text::*;
pub use proto::{BufferState, Operation}; pub use proto::{BufferState, Operation};
@ -213,6 +214,7 @@ pub fn serialize_diagnostics<'a>(
code: entry.diagnostic.code.clone(), code: entry.diagnostic.code.clone(),
is_disk_based: entry.diagnostic.is_disk_based, is_disk_based: entry.diagnostic.is_disk_based,
is_unnecessary: entry.diagnostic.is_unnecessary, is_unnecessary: entry.diagnostic.is_unnecessary,
data: entry.diagnostic.data.as_ref().map(|data| data.to_string()),
}) })
.collect() .collect()
} }
@ -396,6 +398,11 @@ pub fn deserialize_diagnostics(
diagnostics diagnostics
.into_iter() .into_iter()
.filter_map(|diagnostic| { .filter_map(|diagnostic| {
let data = if let Some(data) = diagnostic.data {
Some(Value::from_str(&data).ok()?)
} else {
None
};
Some(DiagnosticEntry { Some(DiagnosticEntry {
range: deserialize_anchor(diagnostic.start?)?..deserialize_anchor(diagnostic.end?)?, range: deserialize_anchor(diagnostic.start?)?..deserialize_anchor(diagnostic.end?)?,
diagnostic: Diagnostic { diagnostic: Diagnostic {
@ -413,6 +420,7 @@ pub fn deserialize_diagnostics(
is_primary: diagnostic.is_primary, is_primary: diagnostic.is_primary,
is_disk_based: diagnostic.is_disk_based, is_disk_based: diagnostic.is_disk_based,
is_unnecessary: diagnostic.is_unnecessary, is_unnecessary: diagnostic.is_unnecessary,
data,
}, },
}) })
}) })

View File

@ -4595,6 +4595,7 @@ impl Project {
is_primary: true, is_primary: true,
is_disk_based, is_disk_based,
is_unnecessary, is_unnecessary,
data: diagnostic.data.clone(),
}, },
}); });
if let Some(infos) = &diagnostic.related_information { if let Some(infos) = &diagnostic.related_information {
@ -4612,6 +4613,7 @@ impl Project {
is_primary: false, is_primary: false,
is_disk_based, is_disk_based,
is_unnecessary: false, is_unnecessary: false,
data: diagnostic.data.clone(),
}, },
}); });
} }

View File

@ -1890,6 +1890,7 @@ message Diagnostic {
Information = 3; Information = 3;
Hint = 4; Hint = 4;
} }
optional string data = 12;
} }
message Operation { message Operation {