From f044059a2a44c65533aa4704fffd911476060c05 Mon Sep 17 00:00:00 2001 From: Triton171 Date: Mon, 28 Feb 2022 09:57:22 +0100 Subject: [PATCH] Implement LSP `workspace/configuration` and `workspace/didChangeConfiguration` (#1684) * Implement LSP `workspace/configuration` request * Implement LSP `workspace/didChangeConfiguration` notification. * Simplify retrieval of LSP configuration * Implement suggestions from PR discussion Co-authored-by: Triton171 --- helix-lsp/src/client.rs | 21 +++++++++++++++++++ helix-lsp/src/lib.rs | 7 +++++++ helix-term/src/application.rs | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 362498cb4..1ce5158be 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -113,6 +113,10 @@ impl Client { self.offset_encoding } + pub fn config(&self) -> Option<&Value> { + self.config.as_ref() + } + /// Execute a RPC request on the language server. async fn request(&self, params: R::Params) -> Result where @@ -246,6 +250,13 @@ impl Client { root_uri: root, initialization_options: self.config.clone(), capabilities: lsp::ClientCapabilities { + workspace: Some(lsp::WorkspaceClientCapabilities { + configuration: Some(true), + did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities { + dynamic_registration: Some(false), + }), + ..Default::default() + }), text_document: Some(lsp::TextDocumentClientCapabilities { completion: Some(lsp::CompletionClientCapabilities { completion_item: Some(lsp::CompletionItemCapability { @@ -330,6 +341,16 @@ impl Client { self.exit().await } + // ------------------------------------------------------------------------------------------- + // Workspace + // ------------------------------------------------------------------------------------------- + + pub fn did_change_configuration(&self, settings: Value) -> impl Future> { + self.notify::( + lsp::DidChangeConfigurationParams { settings }, + ) + } + // ------------------------------------------------------------------------------------------- // Text document // ------------------------------------------------------------------------------------------- diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 109546d05..389dfb4dd 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -191,6 +191,7 @@ pub mod util { pub enum MethodCall { WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams), ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams), + WorkspaceConfiguration(lsp::ConfigurationParams), } impl MethodCall { @@ -209,6 +210,12 @@ impl MethodCall { .expect("Failed to parse ApplyWorkspaceEdit params"); Self::ApplyWorkspaceEdit(params) } + lsp::request::WorkspaceConfiguration::METHOD => { + let params: lsp::ConfigurationParams = params + .parse() + .expect("Failed to parse WorkspaceConfiguration params"); + Self::WorkspaceConfiguration(params) + } _ => { log::warn!("unhandled lsp request: {}", method); return None; diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 986df703e..2a7c9c219 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -532,6 +532,13 @@ impl Application { } }; + // Trigger a workspace/didChangeConfiguration notification after initialization. + // This might not be required by the spec but Neovim does this as well, so it's + // probably a good idea for compatibility. + if let Some(config) = language_server.config() { + tokio::spawn(language_server.did_change_configuration(config.clone())); + } + let docs = self.editor.documents().filter(|doc| { doc.language_server().map(|server| server.id()) == Some(server_id) }); @@ -788,6 +795,37 @@ impl Application { })), )); } + MethodCall::WorkspaceConfiguration(params) => { + let language_server = + match self.editor.language_servers.get_by_id(server_id) { + Some(language_server) => language_server, + None => { + warn!("can't find language server with id `{}`", server_id); + return; + } + }; + let result: Vec<_> = params + .items + .iter() + .map(|item| { + let mut config = match &item.scope_uri { + Some(scope) => { + let path = scope.to_file_path().ok()?; + let doc = self.editor.document_by_path(path)?; + doc.language_config()?.config.as_ref()? + } + None => language_server.config()?, + }; + if let Some(section) = item.section.as_ref() { + for part in section.split('.') { + config = config.get(part)?; + } + } + Some(config) + }) + .collect(); + tokio::spawn(language_server.reply(id, Ok(json!(result)))); + } } } e => unreachable!("{:?}", e),