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_disk_based: false,
is_unnecessary: false,
data: None,
},
}
}

View File

@ -27,6 +27,7 @@ use gpui::{
use lazy_static::lazy_static;
use lsp::LanguageServerId;
use parking_lot::Mutex;
use serde_json::Value;
use similar::{ChangeTag, TextDiff};
use smallvec::SmallVec;
use smol::future::yield_now;
@ -213,6 +214,8 @@ pub struct Diagnostic {
pub is_disk_based: bool,
/// Whether this diagnostic marks unnecessary code.
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.
@ -3844,6 +3847,7 @@ impl Default for Diagnostic {
is_primary: false,
is_disk_based: false,
is_unnecessary: false,
data: None,
}
}
}

View File

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

View File

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

View File

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

View File

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