project: Take 2 on Handle textDocument/didSave and textDocument/didChange (un)registration and usage correctly (#36485)
Relands https://github.com/zed-industries/zed/pull/36441 with a deserialization fix. Previously, deserializing `"includeText"` into `lsp::TextDocumentSyncSaveOptions` resulted in a `Supported(false)` type instead of `SaveOptions(SaveOptions { include_text: Option<bool> })`. ```rs impl From<bool> for TextDocumentSyncSaveOptions { fn from(from: bool) -> Self { Self::Supported(from) } } ``` Looks like, while dynamic registartion we only get `SaveOptions` type and never `Supported` type. (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentSaveRegistrationOptions) Release Notes: - N/A --------- Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
parent
8f567383e4
commit
e3b593efbd
1 changed files with 70 additions and 18 deletions
|
@ -74,8 +74,8 @@ use lsp::{
|
||||||
FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
|
FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
|
||||||
LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId,
|
LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId,
|
||||||
LanguageServerName, LanguageServerSelector, LspRequestFuture, MessageActionItem, MessageType,
|
LanguageServerName, LanguageServerSelector, LspRequestFuture, MessageActionItem, MessageType,
|
||||||
OneOf, RenameFilesParams, SymbolKind, TextEdit, WillRenameFiles, WorkDoneProgressCancelParams,
|
OneOf, RenameFilesParams, SymbolKind, TextDocumentSyncSaveOptions, TextEdit, WillRenameFiles,
|
||||||
WorkspaceFolder, notification::DidRenameFiles,
|
WorkDoneProgressCancelParams, WorkspaceFolder, notification::DidRenameFiles,
|
||||||
};
|
};
|
||||||
use node_runtime::read_package_installed_version;
|
use node_runtime::read_package_installed_version;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -11800,8 +11800,40 @@ impl LspStore {
|
||||||
.transpose()?
|
.transpose()?
|
||||||
{
|
{
|
||||||
server.update_capabilities(|capabilities| {
|
server.update_capabilities(|capabilities| {
|
||||||
|
let mut sync_options =
|
||||||
|
Self::take_text_document_sync_options(capabilities);
|
||||||
|
sync_options.change = Some(sync_kind);
|
||||||
capabilities.text_document_sync =
|
capabilities.text_document_sync =
|
||||||
Some(lsp::TextDocumentSyncCapability::Kind(sync_kind));
|
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
|
||||||
|
});
|
||||||
|
notify_server_capabilities_updated(&server, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"textDocument/didSave" => {
|
||||||
|
if let Some(include_text) = reg
|
||||||
|
.register_options
|
||||||
|
.map(|opts| {
|
||||||
|
let transpose = opts
|
||||||
|
.get("includeText")
|
||||||
|
.cloned()
|
||||||
|
.map(serde_json::from_value::<Option<bool>>)
|
||||||
|
.transpose();
|
||||||
|
match transpose {
|
||||||
|
Ok(value) => Ok(value.flatten()),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
{
|
||||||
|
server.update_capabilities(|capabilities| {
|
||||||
|
let mut sync_options =
|
||||||
|
Self::take_text_document_sync_options(capabilities);
|
||||||
|
sync_options.save =
|
||||||
|
Some(TextDocumentSyncSaveOptions::SaveOptions(lsp::SaveOptions {
|
||||||
|
include_text,
|
||||||
|
}));
|
||||||
|
capabilities.text_document_sync =
|
||||||
|
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
|
||||||
});
|
});
|
||||||
notify_server_capabilities_updated(&server, cx);
|
notify_server_capabilities_updated(&server, cx);
|
||||||
}
|
}
|
||||||
|
@ -11953,7 +11985,19 @@ impl LspStore {
|
||||||
}
|
}
|
||||||
"textDocument/didChange" => {
|
"textDocument/didChange" => {
|
||||||
server.update_capabilities(|capabilities| {
|
server.update_capabilities(|capabilities| {
|
||||||
capabilities.text_document_sync = None;
|
let mut sync_options = Self::take_text_document_sync_options(capabilities);
|
||||||
|
sync_options.change = None;
|
||||||
|
capabilities.text_document_sync =
|
||||||
|
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
|
||||||
|
});
|
||||||
|
notify_server_capabilities_updated(&server, cx);
|
||||||
|
}
|
||||||
|
"textDocument/didSave" => {
|
||||||
|
server.update_capabilities(|capabilities| {
|
||||||
|
let mut sync_options = Self::take_text_document_sync_options(capabilities);
|
||||||
|
sync_options.save = None;
|
||||||
|
capabilities.text_document_sync =
|
||||||
|
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
|
||||||
});
|
});
|
||||||
notify_server_capabilities_updated(&server, cx);
|
notify_server_capabilities_updated(&server, cx);
|
||||||
}
|
}
|
||||||
|
@ -11981,6 +12025,20 @@ impl LspStore {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_text_document_sync_options(
|
||||||
|
capabilities: &mut lsp::ServerCapabilities,
|
||||||
|
) -> lsp::TextDocumentSyncOptions {
|
||||||
|
match capabilities.text_document_sync.take() {
|
||||||
|
Some(lsp::TextDocumentSyncCapability::Options(sync_options)) => sync_options,
|
||||||
|
Some(lsp::TextDocumentSyncCapability::Kind(sync_kind)) => {
|
||||||
|
let mut sync_options = lsp::TextDocumentSyncOptions::default();
|
||||||
|
sync_options.change = Some(sync_kind);
|
||||||
|
sync_options
|
||||||
|
}
|
||||||
|
None => lsp::TextDocumentSyncOptions::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registration with empty capabilities should be ignored.
|
// Registration with empty capabilities should be ignored.
|
||||||
|
@ -13083,24 +13141,18 @@ async fn populate_labels_for_symbols(
|
||||||
|
|
||||||
fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
|
fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
|
||||||
match server.capabilities().text_document_sync.as_ref()? {
|
match server.capabilities().text_document_sync.as_ref()? {
|
||||||
lsp::TextDocumentSyncCapability::Kind(kind) => match *kind {
|
lsp::TextDocumentSyncCapability::Options(opts) => match opts.save.as_ref()? {
|
||||||
lsp::TextDocumentSyncKind::NONE => None,
|
// Server wants didSave but didn't specify includeText.
|
||||||
lsp::TextDocumentSyncKind::FULL => Some(true),
|
lsp::TextDocumentSyncSaveOptions::Supported(true) => Some(false),
|
||||||
lsp::TextDocumentSyncKind::INCREMENTAL => Some(false),
|
// Server doesn't want didSave at all.
|
||||||
_ => None,
|
lsp::TextDocumentSyncSaveOptions::Supported(false) => None,
|
||||||
},
|
// Server provided SaveOptions.
|
||||||
lsp::TextDocumentSyncCapability::Options(options) => match options.save.as_ref()? {
|
|
||||||
lsp::TextDocumentSyncSaveOptions::Supported(supported) => {
|
|
||||||
if *supported {
|
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lsp::TextDocumentSyncSaveOptions::SaveOptions(save_options) => {
|
lsp::TextDocumentSyncSaveOptions::SaveOptions(save_options) => {
|
||||||
Some(save_options.include_text.unwrap_or(false))
|
Some(save_options.include_text.unwrap_or(false))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// We do not have any save info. Kind affects didChange only.
|
||||||
|
lsp::TextDocumentSyncCapability::Kind(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue