Ignore capability registrations with empty capabilities (#36000)

Kirill Bulatov created

Change summary

crates/project/src/lsp_store.rs | 268 +++++++++++++++++-----------------
1 file changed, 133 insertions(+), 135 deletions(-)

Detailed changes

crates/project/src/lsp_store.rs 🔗

@@ -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>) {
     if let Some(capabilities) = serde_json::to_string(&server.capabilities()).ok() {
         cx.emit(LspStoreEvent::LanguageServerUpdate {
@@ -11690,190 +11676,190 @@ impl LspStore {
                     // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
                 }
                 "workspace/symbol" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.workspace_symbol_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.workspace_symbol_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "workspace/fileOperations" => {
-                    let caps = reg
-                        .register_options
-                        .map(serde_json::from_value)
-                        .transpose()?
-                        .unwrap_or_default();
-                    server.update_capabilities(|capabilities| {
-                        capabilities
-                            .workspace
-                            .get_or_insert_default()
-                            .file_operations = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = reg.register_options {
+                        let caps = serde_json::from_value(options)?;
+                        server.update_capabilities(|capabilities| {
+                            capabilities
+                                .workspace
+                                .get_or_insert_default()
+                                .file_operations = Some(caps);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "workspace/executeCommand" => {
-                    let options = reg
-                        .register_options
-                        .map(serde_json::from_value)
-                        .transpose()?
-                        .unwrap_or_default();
-                    server.update_capabilities(|capabilities| {
-                        capabilities.execute_command_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = reg.register_options {
+                        let options = serde_json::from_value(options)?;
+                        server.update_capabilities(|capabilities| {
+                            capabilities.execute_command_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/rangeFormatting" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.document_range_formatting_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.document_range_formatting_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/onTypeFormatting" => {
-                    let options = reg
+                    if let Some(options) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_default();
-                    server.update_capabilities(|capabilities| {
-                        capabilities.document_on_type_formatting_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.document_on_type_formatting_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/formatting" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.document_formatting_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.document_formatting_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/rename" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.rename_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.rename_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/inlayHint" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.inlay_hint_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.inlay_hint_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/documentSymbol" => {
-                    let options = parse_register_capabilities(reg)?;
-                    server.update_capabilities(|capabilities| {
-                        capabilities.document_symbol_provider = Some(options);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    if let Some(options) = parse_register_capabilities(reg)? {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.document_symbol_provider = Some(options);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "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);
+                        .transpose()?
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.code_action_provider =
+                                Some(lsp::CodeActionProviderCapability::Options(options));
+                        });
+                        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);
+                    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" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_default();
-                    server.update_capabilities(|capabilities| {
-                        capabilities.completion_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.completion_provider = Some(caps);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/hover" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_else(|| lsp::HoverProviderCapability::Simple(true));
-                    server.update_capabilities(|capabilities| {
-                        capabilities.hover_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.hover_provider = Some(caps);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/signatureHelp" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_default();
-                    server.update_capabilities(|capabilities| {
-                        capabilities.signature_help_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.signature_help_provider = Some(caps);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/synchronization" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_else(|| {
-                            lsp::TextDocumentSyncCapability::Options(
-                                lsp::TextDocumentSyncOptions::default(),
-                            )
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.text_document_sync = Some(caps);
                         });
-                    server.update_capabilities(|capabilities| {
-                        capabilities.text_document_sync = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/codeLens" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_else(|| lsp::CodeLensOptions {
-                            resolve_provider: None,
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.code_lens_provider = Some(caps);
                         });
-                    server.update_capabilities(|capabilities| {
-                        capabilities.code_lens_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/diagnostic" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_else(|| {
-                            lsp::DiagnosticServerCapabilities::RegistrationOptions(
-                                lsp::DiagnosticRegistrationOptions::default(),
-                            )
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.diagnostic_provider = Some(caps);
                         });
-                    server.update_capabilities(|capabilities| {
-                        capabilities.diagnostic_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 "textDocument/colorProvider" => {
-                    let caps = reg
+                    if let Some(caps) = reg
                         .register_options
                         .map(serde_json::from_value)
                         .transpose()?
-                        .unwrap_or_else(|| lsp::ColorProviderCapability::Simple(true));
-                    server.update_capabilities(|capabilities| {
-                        capabilities.color_provider = Some(caps);
-                    });
-                    notify_server_capabilities_updated(&server, cx);
+                    {
+                        server.update_capabilities(|capabilities| {
+                            capabilities.color_provider = Some(caps);
+                        });
+                        notify_server_capabilities_updated(&server, cx);
+                    }
                 }
                 _ => 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(
     languages: &Arc<LanguageRegistry>,
     cx: &mut Context<'_, LspStore>,