Ignore capability registrations with empty capabilities (#36000)

This commit is contained in:
Kirill Bulatov 2025-08-12 07:53:20 +03:00 committed by GitHub
parent b35e69692d
commit 481e3e5092
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3367,20 +3367,6 @@ impl LocalLspStore {
} }
} }
fn parse_register_capabilities<T: serde::de::DeserializeOwned>(
reg: lsp::Registration,
) -> anyhow::Result<OneOf<bool, T>> {
let caps = match reg
.register_options
.map(|options| serde_json::from_value::<T>(options))
.transpose()?
{
None => OneOf::Left(true),
Some(options) => OneOf::Right(options),
};
Ok(caps)
}
fn notify_server_capabilities_updated(server: &LanguageServer, cx: &mut Context<LspStore>) { fn notify_server_capabilities_updated(server: &LanguageServer, cx: &mut Context<LspStore>) {
if let Some(capabilities) = serde_json::to_string(&server.capabilities()).ok() { if let Some(capabilities) = serde_json::to_string(&server.capabilities()).ok() {
cx.emit(LspStoreEvent::LanguageServerUpdate { cx.emit(LspStoreEvent::LanguageServerUpdate {
@ -11690,18 +11676,16 @@ impl LspStore {
// Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings. // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
} }
"workspace/symbol" => { "workspace/symbol" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.workspace_symbol_provider = Some(options); capabilities.workspace_symbol_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"workspace/fileOperations" => { "workspace/fileOperations" => {
let caps = reg if let Some(options) = reg.register_options {
.register_options let caps = serde_json::from_value(options)?;
.map(serde_json::from_value)
.transpose()?
.unwrap_or_default();
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities capabilities
.workspace .workspace
@ -11710,171 +11694,173 @@ impl LspStore {
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"workspace/executeCommand" => { "workspace/executeCommand" => {
let options = reg if let Some(options) = reg.register_options {
.register_options let options = serde_json::from_value(options)?;
.map(serde_json::from_value)
.transpose()?
.unwrap_or_default();
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.execute_command_provider = Some(options); capabilities.execute_command_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/rangeFormatting" => { "textDocument/rangeFormatting" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.document_range_formatting_provider = Some(options); capabilities.document_range_formatting_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/onTypeFormatting" => { "textDocument/onTypeFormatting" => {
let options = reg if let Some(options) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_default(); {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.document_on_type_formatting_provider = Some(options); capabilities.document_on_type_formatting_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/formatting" => { "textDocument/formatting" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.document_formatting_provider = Some(options); capabilities.document_formatting_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/rename" => { "textDocument/rename" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.rename_provider = Some(options); capabilities.rename_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/inlayHint" => { "textDocument/inlayHint" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.inlay_hint_provider = Some(options); capabilities.inlay_hint_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/documentSymbol" => { "textDocument/documentSymbol" => {
let options = parse_register_capabilities(reg)?; if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.document_symbol_provider = Some(options); capabilities.document_symbol_provider = Some(options);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/codeAction" => { "textDocument/codeAction" => {
let options = reg if let Some(options) = reg
.register_options
.map(serde_json::from_value)
.transpose()?;
let provider_capability = match options {
None => lsp::CodeActionProviderCapability::Simple(true),
Some(options) => lsp::CodeActionProviderCapability::Options(options),
};
server.update_capabilities(|capabilities| {
capabilities.code_action_provider = Some(provider_capability);
});
notify_server_capabilities_updated(&server, cx);
}
"textDocument/definition" => {
let caps = parse_register_capabilities(reg)?;
server.update_capabilities(|capabilities| {
capabilities.definition_provider = Some(caps);
});
notify_server_capabilities_updated(&server, cx);
}
"textDocument/completion" => {
let caps = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_default(); {
server.update_capabilities(|capabilities| {
capabilities.code_action_provider =
Some(lsp::CodeActionProviderCapability::Options(options));
});
notify_server_capabilities_updated(&server, cx);
}
}
"textDocument/definition" => {
if let Some(options) = parse_register_capabilities(reg)? {
server.update_capabilities(|capabilities| {
capabilities.definition_provider = Some(options);
});
notify_server_capabilities_updated(&server, cx);
}
}
"textDocument/completion" => {
if let Some(caps) = reg
.register_options
.map(serde_json::from_value)
.transpose()?
{
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.completion_provider = Some(caps); capabilities.completion_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/hover" => { "textDocument/hover" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_else(|| lsp::HoverProviderCapability::Simple(true)); {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.hover_provider = Some(caps); capabilities.hover_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/signatureHelp" => { "textDocument/signatureHelp" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_default(); {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.signature_help_provider = Some(caps); capabilities.signature_help_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/synchronization" => { "textDocument/synchronization" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_else(|| { {
lsp::TextDocumentSyncCapability::Options(
lsp::TextDocumentSyncOptions::default(),
)
});
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.text_document_sync = Some(caps); capabilities.text_document_sync = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/codeLens" => { "textDocument/codeLens" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_else(|| lsp::CodeLensOptions { {
resolve_provider: None,
});
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.code_lens_provider = Some(caps); capabilities.code_lens_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/diagnostic" => { "textDocument/diagnostic" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_else(|| { {
lsp::DiagnosticServerCapabilities::RegistrationOptions(
lsp::DiagnosticRegistrationOptions::default(),
)
});
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.diagnostic_provider = Some(caps); capabilities.diagnostic_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
"textDocument/colorProvider" => { "textDocument/colorProvider" => {
let caps = reg if let Some(caps) = reg
.register_options .register_options
.map(serde_json::from_value) .map(serde_json::from_value)
.transpose()? .transpose()?
.unwrap_or_else(|| lsp::ColorProviderCapability::Simple(true)); {
server.update_capabilities(|capabilities| { server.update_capabilities(|capabilities| {
capabilities.color_provider = Some(caps); capabilities.color_provider = Some(caps);
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
}
_ => log::warn!("unhandled capability registration: {reg:?}"), _ => log::warn!("unhandled capability registration: {reg:?}"),
} }
} }
@ -12016,6 +12002,18 @@ impl LspStore {
} }
} }
// Registration with empty capabilities should be ignored.
// https://github.com/microsoft/vscode-languageserver-node/blob/d90a87f9557a0df9142cfb33e251cfa6fe27d970/client/src/common/formatting.ts#L67-L70
fn parse_register_capabilities<T: serde::de::DeserializeOwned>(
reg: lsp::Registration,
) -> anyhow::Result<Option<OneOf<bool, T>>> {
Ok(reg
.register_options
.map(|options| serde_json::from_value::<T>(options))
.transpose()?
.map(OneOf::Right))
}
fn subscribe_to_binary_statuses( fn subscribe_to_binary_statuses(
languages: &Arc<LanguageRegistry>, languages: &Arc<LanguageRegistry>,
cx: &mut Context<'_, LspStore>, cx: &mut Context<'_, LspStore>,