on master help sign in
Implement LSP `workspace/configuration` and `workspace/didChangeConfiguration` (#1684)
f044059Triton171
helix-lsp/src/client.rs+21
@@ -113,6 +113,10 @@ impl Client {
113 self.offset_encoding
113 self.offset_encoding
114 }
114 }
115
115
116+ pub fn config(&self) -> Option<&Value> {
117+ self.config.as_ref()
118+ }
119+
116 /// Execute a RPC request on the language server.
120 /// Execute a RPC request on the language server.
117 async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
121 async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
118 where
122 where
@@ -246,6 +250,13 @@ impl Client {
246 root_uri: root,
250 root_uri: root,
247 initialization_options: self.config.clone(),
251 initialization_options: self.config.clone(),
248 capabilities: lsp::ClientCapabilities {
252 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+ }),
249 text_document: Some(lsp::TextDocumentClientCapabilities {
260 text_document: Some(lsp::TextDocumentClientCapabilities {
250 completion: Some(lsp::CompletionClientCapabilities {
261 completion: Some(lsp::CompletionClientCapabilities {
251 completion_item: Some(lsp::CompletionItemCapability {
262 completion_item: Some(lsp::CompletionItemCapability {
@@ -330,6 +341,16 @@ impl Client {
330 self.exit().await
341 self.exit().await
331 }
342 }
332
343
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+
333 // -------------------------------------------------------------------------------------------
354 // -------------------------------------------------------------------------------------------
334 // Text document
355 // Text document
335 // -------------------------------------------------------------------------------------------
356 // -------------------------------------------------------------------------------------------
helix-lsp/src/lib.rs+7
@@ -191,6 +191,7 @@ pub mod util {
191pub enum MethodCall {
191pub enum MethodCall {
192 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
192 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
193 ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
193 ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
194+ WorkspaceConfiguration(lsp::ConfigurationParams),
194}
195}
195
196
196impl MethodCall {
197impl MethodCall {
@@ -209,6 +210,12 @@ impl MethodCall {
209 .expect("Failed to parse ApplyWorkspaceEdit params");
210 .expect("Failed to parse ApplyWorkspaceEdit params");
210 Self::ApplyWorkspaceEdit(params)
211 Self::ApplyWorkspaceEdit(params)
211 }
212 }
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+ }
212 _ => {
219 _ => {
213 log::warn!("unhandled lsp request: {}", method);
220 log::warn!("unhandled lsp request: {}", method);
214 return None;
221 return None;
helix-term/src/application.rs+38
@@ -532,6 +532,13 @@ impl Application {
532 }
532 }
533 };
533 };
534
534
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+
535 let docs = self.editor.documents().filter(|doc| {
542 let docs = self.editor.documents().filter(|doc| {
536 doc.language_server().map(|server| server.id()) == Some(server_id)
543 doc.language_server().map(|server| server.id()) == Some(server_id)
537 });
544 });
@@ -788,6 +795,37 @@ impl Application {
788 })),
795 })),
789 ));
796 ));
790 }
797 }
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+ }
791 }
829 }
792 }
830 }
793 e => unreachable!("{:?}", e),
831 e => unreachable!("{:?}", e),