on master help sign in
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 <triton0171@gmail.com>

commitf044059a2a44c65533aa4704fffd911476060c05 authorTriton171 <daniel.ebert@protonmail.com> dateMon 28 Feb 2022, 09:57 (5 years ago) parentc15996a
3 files changed+66
helix-lsp/src/client.rs+21 helix-lsp/src/lib.rs+7 helix-term/src/application.rs+38
helix-lsp/src/client.rs+21
@@ -113,6 +113,10 @@ impl Client {
113113 self.offset_encoding
114114 }
115115
116+ pub fn config(&self) -> Option<&Value> {
117+ self.config.as_ref()
118+ }
119+
116120 /// Execute a RPC request on the language server.
117121 async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
118122 where
@@ -246,6 +250,13 @@ impl Client {
246250 root_uri: root,
247251 initialization_options: self.config.clone(),
248252 capabilities: lsp::ClientCapabilities {
253+ workspace: Some(lsp::WorkspaceClientCapabilities {
254+ configuration: Some(true),
255+ did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities {
256+ dynamic_registration: Some(false),
257+ }),
258+ ..Default::default()
259+ }),
249260 text_document: Some(lsp::TextDocumentClientCapabilities {
250261 completion: Some(lsp::CompletionClientCapabilities {
251262 completion_item: Some(lsp::CompletionItemCapability {
@@ -330,6 +341,16 @@ impl Client {
330341 self.exit().await
331342 }
332343
344+ // -------------------------------------------------------------------------------------------
345+ // Workspace
346+ // -------------------------------------------------------------------------------------------
347+
348+ pub fn did_change_configuration(&self, settings: Value) -> impl Future<Output = Result<()>> {
349+ self.notify::<lsp::notification::DidChangeConfiguration>(
350+ lsp::DidChangeConfigurationParams { settings },
351+ )
352+ }
353+
333354 // -------------------------------------------------------------------------------------------
334355 // Text document
335356 // -------------------------------------------------------------------------------------------
helix-lsp/src/lib.rs+7
@@ -191,6 +191,7 @@ pub mod util {
191191pub enum MethodCall {
192192 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
193193 ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
194+ WorkspaceConfiguration(lsp::ConfigurationParams),
194195}
195196
196197impl MethodCall {
@@ -209,6 +210,12 @@ impl MethodCall {
209210 .expect("Failed to parse ApplyWorkspaceEdit params");
210211 Self::ApplyWorkspaceEdit(params)
211212 }
213+ lsp::request::WorkspaceConfiguration::METHOD => {
214+ let params: lsp::ConfigurationParams = params
215+ .parse()
216+ .expect("Failed to parse WorkspaceConfiguration params");
217+ Self::WorkspaceConfiguration(params)
218+ }
212219 _ => {
213220 log::warn!("unhandled lsp request: {}", method);
214221 return None;
helix-term/src/application.rs+38
@@ -532,6 +532,13 @@ impl Application {
532532 }
533533 };
534534
535+ // Trigger a workspace/didChangeConfiguration notification after initialization.
536+ // This might not be required by the spec but Neovim does this as well, so it's
537+ // probably a good idea for compatibility.
538+ if let Some(config) = language_server.config() {
539+ tokio::spawn(language_server.did_change_configuration(config.clone()));
540+ }
541+
535542 let docs = self.editor.documents().filter(|doc| {
536543 doc.language_server().map(|server| server.id()) == Some(server_id)
537544 });
@@ -788,6 +795,37 @@ impl Application {
788795 })),
789796 ));
790797 }
798+ MethodCall::WorkspaceConfiguration(params) => {
799+ let language_server =
800+ match self.editor.language_servers.get_by_id(server_id) {
801+ Some(language_server) => language_server,
802+ None => {
803+ warn!("can't find language server with id `{}`", server_id);
804+ return;
805+ }
806+ };
807+ let result: Vec<_> = params
808+ .items
809+ .iter()
810+ .map(|item| {
811+ let mut config = match &item.scope_uri {
812+ Some(scope) => {
813+ let path = scope.to_file_path().ok()?;
814+ let doc = self.editor.document_by_path(path)?;
815+ doc.language_config()?.config.as_ref()?
816+ }
817+ None => language_server.config()?,
818+ };
819+ if let Some(section) = item.section.as_ref() {
820+ for part in section.split('.') {
821+ config = config.get(part)?;
822+ }
823+ }
824+ Some(config)
825+ })
826+ .collect();
827+ tokio::spawn(language_server.reply(id, Ok(json!(result))));
828+ }
791829 }
792830 }
793831 e => unreachable!("{:?}", e),