lsp: Implement support for workspace_folders (currently just one)

Refs #1898
pull/1925/head
Blaž Hrastnik 3 years ago
parent 236c6b7707
commit 8adf0c1b3a
No known key found for this signature in database
GPG Key ID: 1238B9C4AD889640

@ -31,7 +31,9 @@ pub struct Client {
pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>, pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
offset_encoding: OffsetEncoding, offset_encoding: OffsetEncoding,
config: Option<Value>, config: Option<Value>,
root_markers: Vec<String>, root_path: Option<std::path::PathBuf>,
root_uri: Option<lsp::Url>,
workspace_folders: Vec<lsp::WorkspaceFolder>,
} }
impl Client { impl Client {
@ -40,7 +42,7 @@ impl Client {
cmd: &str, cmd: &str,
args: &[String], args: &[String],
config: Option<Value>, config: Option<Value>,
root_markers: Vec<String>, root_markers: &[String],
id: usize, id: usize,
) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> { ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
// Resolve path to the binary // Resolve path to the binary
@ -65,6 +67,27 @@ impl Client {
let (server_rx, server_tx, initialize_notify) = let (server_rx, server_tx, initialize_notify) =
Transport::start(reader, writer, stderr, id); Transport::start(reader, writer, stderr, id);
let root_path = find_root(None, root_markers);
let root_uri = root_path
.clone()
.and_then(|root| lsp::Url::from_file_path(root).ok());
// TODO: support multiple workspace folders
let workspace_folders = root_uri
.clone()
.map(|root| {
vec![lsp::WorkspaceFolder {
name: root
.path_segments()
.and_then(|segments| segments.last())
.map(|basename| basename.to_string())
.unwrap_or_default(),
uri: root,
}]
})
.unwrap_or_default();
let client = Self { let client = Self {
id, id,
_process: process, _process: process,
@ -73,7 +96,10 @@ impl Client {
capabilities: OnceCell::new(), capabilities: OnceCell::new(),
offset_encoding: OffsetEncoding::Utf8, offset_encoding: OffsetEncoding::Utf8,
config, config,
root_markers,
root_path,
root_uri,
workspace_folders,
}; };
Ok((client, server_rx, initialize_notify)) Ok((client, server_rx, initialize_notify))
@ -117,6 +143,10 @@ impl Client {
self.config.as_ref() self.config.as_ref()
} }
pub fn workspace_folders(&self) -> &[lsp::WorkspaceFolder] {
&self.workspace_folders
}
/// 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
@ -234,13 +264,6 @@ impl Client {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> { pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
// TODO: delay any requests that are triggered prior to initialize
let root_path = find_root(None, &self.root_markers);
let root_uri = root_path
.clone()
.and_then(|root| lsp::Url::from_file_path(root).ok());
if self.config.is_some() { if self.config.is_some() {
log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap()); log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
} }
@ -248,10 +271,14 @@ impl Client {
#[allow(deprecated)] #[allow(deprecated)]
let params = lsp::InitializeParams { let params = lsp::InitializeParams {
process_id: Some(std::process::id()), process_id: Some(std::process::id()),
workspace_folders: Some(self.workspace_folders.clone()),
// root_path is obsolete, but some clients like pyright still use it so we specify both. // root_path is obsolete, but some clients like pyright still use it so we specify both.
// clients will prefer _uri if possible // clients will prefer _uri if possible
root_path: root_path.and_then(|path| path.to_str().map(|path| path.to_owned())), root_path: self
root_uri, .root_path
.clone()
.and_then(|path| path.to_str().map(|path| path.to_owned())),
root_uri: self.root_uri.clone(),
initialization_options: self.config.clone(), initialization_options: self.config.clone(),
capabilities: lsp::ClientCapabilities { capabilities: lsp::ClientCapabilities {
workspace: Some(lsp::WorkspaceClientCapabilities { workspace: Some(lsp::WorkspaceClientCapabilities {
@ -259,6 +286,7 @@ impl Client {
did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities { did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities {
dynamic_registration: Some(false), dynamic_registration: Some(false),
}), }),
workspace_folders: Some(true),
..Default::default() ..Default::default()
}), }),
text_document: Some(lsp::TextDocumentClientCapabilities { text_document: Some(lsp::TextDocumentClientCapabilities {
@ -314,7 +342,6 @@ impl Client {
..Default::default() ..Default::default()
}, },
trace: None, trace: None,
workspace_folders: None,
client_info: None, client_info: None,
locale: None, // TODO locale: None, // TODO
}; };

@ -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),
WorkspaceFolders,
WorkspaceConfiguration(lsp::ConfigurationParams), WorkspaceConfiguration(lsp::ConfigurationParams),
} }
@ -210,6 +211,7 @@ impl MethodCall {
.expect("Failed to parse ApplyWorkspaceEdit params"); .expect("Failed to parse ApplyWorkspaceEdit params");
Self::ApplyWorkspaceEdit(params) Self::ApplyWorkspaceEdit(params)
} }
lsp::request::WorkspaceFoldersRequest::METHOD => Self::WorkspaceFolders,
lsp::request::WorkspaceConfiguration::METHOD => { lsp::request::WorkspaceConfiguration::METHOD => {
let params: lsp::ConfigurationParams = params let params: lsp::ConfigurationParams = params
.parse() .parse()
@ -320,7 +322,7 @@ impl Registry {
&config.command, &config.command,
&config.args, &config.args,
language_config.config.clone(), language_config.config.clone(),
language_config.roots.clone(), &language_config.roots,
id, id,
)?; )?;
self.incoming.push(UnboundedReceiverStream::new(incoming)); self.incoming.push(UnboundedReceiverStream::new(incoming));

@ -601,7 +601,7 @@ impl Application {
} }
}; };
match call { let reply = match call {
MethodCall::WorkDoneProgressCreate(params) => { MethodCall::WorkDoneProgressCreate(params) => {
self.lsp_progress.create(server_id, params.token); self.lsp_progress.create(server_id, params.token);
@ -613,16 +613,8 @@ impl Application {
if spinner.is_stopped() { if spinner.is_stopped() {
spinner.start(); spinner.start();
} }
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;
}
};
tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null))); Ok(serde_json::Value::Null)
} }
MethodCall::ApplyWorkspaceEdit(params) => { MethodCall::ApplyWorkspaceEdit(params) => {
apply_workspace_edit( apply_workspace_edit(
@ -631,33 +623,19 @@ impl Application {
&params.edit, &params.edit,
); );
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;
}
};
tokio::spawn(language_server.reply(
id,
Ok(json!(lsp::ApplyWorkspaceEditResponse { Ok(json!(lsp::ApplyWorkspaceEditResponse {
applied: true, applied: true,
failure_reason: None, failure_reason: None,
failed_change: None, failed_change: None,
})), }))
));
} }
MethodCall::WorkspaceConfiguration(params) => { MethodCall::WorkspaceFolders => {
let language_server = let language_server =
match self.editor.language_servers.get_by_id(server_id) { self.editor.language_servers.get_by_id(server_id).unwrap();
Some(language_server) => language_server,
None => { Ok(json!(language_server.workspace_folders()))
warn!("can't find language server with id `{}`", server_id);
return;
} }
}; MethodCall::WorkspaceConfiguration(params) => {
let result: Vec<_> = params let result: Vec<_> = params
.items .items
.iter() .iter()
@ -668,7 +646,12 @@ impl Application {
let doc = self.editor.document_by_path(path)?; let doc = self.editor.document_by_path(path)?;
doc.language_config()?.config.as_ref()? doc.language_config()?.config.as_ref()?
} }
None => language_server.config()?, None => self
.editor
.language_servers
.get_by_id(server_id)
.unwrap()
.config()?,
}; };
if let Some(section) = item.section.as_ref() { if let Some(section) = item.section.as_ref() {
for part in section.split('.') { for part in section.split('.') {
@ -678,9 +661,19 @@ impl Application {
Some(config) Some(config)
}) })
.collect(); .collect();
tokio::spawn(language_server.reply(id, Ok(json!(result)))); Ok(json!(result))
} }
};
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;
} }
};
tokio::spawn(language_server.reply(id, reply));
} }
Call::Invalid { id } => log::error!("LSP invalid method call id={:?}", id), Call::Invalid { id } => log::error!("LSP invalid method call id={:?}", id),
} }

Loading…
Cancel
Save