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>
This commit is contained in:
Triton171 2022-02-28 09:57:22 +01:00 committed by GitHub
parent c15996aff5
commit f044059a2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 0 deletions

View file

@ -113,6 +113,10 @@ impl Client {
self.offset_encoding self.offset_encoding
} }
pub fn config(&self) -> Option<&Value> {
self.config.as_ref()
}
/// Execute a RPC request on the language server. /// Execute a RPC request on the language server.
async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result> async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
where where
@ -246,6 +250,13 @@ impl Client {
root_uri: root, root_uri: root,
initialization_options: self.config.clone(), initialization_options: self.config.clone(),
capabilities: lsp::ClientCapabilities { 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 { text_document: Some(lsp::TextDocumentClientCapabilities {
completion: Some(lsp::CompletionClientCapabilities { completion: Some(lsp::CompletionClientCapabilities {
completion_item: Some(lsp::CompletionItemCapability { completion_item: Some(lsp::CompletionItemCapability {
@ -330,6 +341,16 @@ impl Client {
self.exit().await self.exit().await
} }
// -------------------------------------------------------------------------------------------
// Workspace
// -------------------------------------------------------------------------------------------
pub fn did_change_configuration(&self, settings: Value) -> impl Future<Output = Result<()>> {
self.notify::<lsp::notification::DidChangeConfiguration>(
lsp::DidChangeConfigurationParams { settings },
)
}
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
// Text document // Text document
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------

View file

@ -191,6 +191,7 @@ pub mod util {
pub enum MethodCall { pub enum MethodCall {
WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams), WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams), ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
WorkspaceConfiguration(lsp::ConfigurationParams),
} }
impl MethodCall { impl MethodCall {
@ -209,6 +210,12 @@ impl MethodCall {
.expect("Failed to parse ApplyWorkspaceEdit params"); .expect("Failed to parse ApplyWorkspaceEdit params");
Self::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); log::warn!("unhandled lsp request: {}", method);
return None; return None;

View file

@ -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| { let docs = self.editor.documents().filter(|doc| {
doc.language_server().map(|server| server.id()) == Some(server_id) 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), e => unreachable!("{:?}", e),