mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 09:26:19 +04:00
lsp: Implement support for workspace_folders (currently just one)
Refs #1898
This commit is contained in:
parent
236c6b7707
commit
8adf0c1b3a
@ -31,7 +31,9 @@ pub struct Client {
|
||||
pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
|
||||
offset_encoding: OffsetEncoding,
|
||||
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 {
|
||||
@ -40,7 +42,7 @@ pub fn start(
|
||||
cmd: &str,
|
||||
args: &[String],
|
||||
config: Option<Value>,
|
||||
root_markers: Vec<String>,
|
||||
root_markers: &[String],
|
||||
id: usize,
|
||||
) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
|
||||
// Resolve path to the binary
|
||||
@ -65,6 +67,27 @@ pub fn start(
|
||||
let (server_rx, server_tx, initialize_notify) =
|
||||
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 {
|
||||
id,
|
||||
_process: process,
|
||||
@ -73,7 +96,10 @@ pub fn start(
|
||||
capabilities: OnceCell::new(),
|
||||
offset_encoding: OffsetEncoding::Utf8,
|
||||
config,
|
||||
root_markers,
|
||||
|
||||
root_path,
|
||||
root_uri,
|
||||
workspace_folders,
|
||||
};
|
||||
|
||||
Ok((client, server_rx, initialize_notify))
|
||||
@ -117,6 +143,10 @@ pub fn config(&self) -> Option<&Value> {
|
||||
self.config.as_ref()
|
||||
}
|
||||
|
||||
pub fn workspace_folders(&self) -> &[lsp::WorkspaceFolder] {
|
||||
&self.workspace_folders
|
||||
}
|
||||
|
||||
/// Execute a RPC request on the language server.
|
||||
async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
|
||||
where
|
||||
@ -234,13 +264,6 @@ pub fn reply(
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
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() {
|
||||
log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
|
||||
}
|
||||
@ -248,10 +271,14 @@ pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
|
||||
#[allow(deprecated)]
|
||||
let params = lsp::InitializeParams {
|
||||
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.
|
||||
// clients will prefer _uri if possible
|
||||
root_path: root_path.and_then(|path| path.to_str().map(|path| path.to_owned())),
|
||||
root_uri,
|
||||
root_path: self
|
||||
.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(),
|
||||
capabilities: lsp::ClientCapabilities {
|
||||
workspace: Some(lsp::WorkspaceClientCapabilities {
|
||||
@ -259,6 +286,7 @@ pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
|
||||
did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities {
|
||||
dynamic_registration: Some(false),
|
||||
}),
|
||||
workspace_folders: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
text_document: Some(lsp::TextDocumentClientCapabilities {
|
||||
@ -314,7 +342,6 @@ pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
|
||||
..Default::default()
|
||||
},
|
||||
trace: None,
|
||||
workspace_folders: None,
|
||||
client_info: None,
|
||||
locale: None, // TODO
|
||||
};
|
||||
|
@ -191,6 +191,7 @@ fn from(fmt: LspFormatting) -> Transaction {
|
||||
pub enum MethodCall {
|
||||
WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
|
||||
ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
|
||||
WorkspaceFolders,
|
||||
WorkspaceConfiguration(lsp::ConfigurationParams),
|
||||
}
|
||||
|
||||
@ -210,6 +211,7 @@ pub fn parse(method: &str, params: jsonrpc::Params) -> Option<MethodCall> {
|
||||
.expect("Failed to parse ApplyWorkspaceEdit params");
|
||||
Self::ApplyWorkspaceEdit(params)
|
||||
}
|
||||
lsp::request::WorkspaceFoldersRequest::METHOD => Self::WorkspaceFolders,
|
||||
lsp::request::WorkspaceConfiguration::METHOD => {
|
||||
let params: lsp::ConfigurationParams = params
|
||||
.parse()
|
||||
@ -320,7 +322,7 @@ pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Cli
|
||||
&config.command,
|
||||
&config.args,
|
||||
language_config.config.clone(),
|
||||
language_config.roots.clone(),
|
||||
&language_config.roots,
|
||||
id,
|
||||
)?;
|
||||
self.incoming.push(UnboundedReceiverStream::new(incoming));
|
||||
|
@ -601,7 +601,7 @@ pub async fn handle_language_server_message(
|
||||
}
|
||||
};
|
||||
|
||||
match call {
|
||||
let reply = match call {
|
||||
MethodCall::WorkDoneProgressCreate(params) => {
|
||||
self.lsp_progress.create(server_id, params.token);
|
||||
|
||||
@ -613,16 +613,8 @@ pub async fn handle_language_server_message(
|
||||
if spinner.is_stopped() {
|
||||
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) => {
|
||||
apply_workspace_edit(
|
||||
@ -631,33 +623,19 @@ pub async fn handle_language_server_message(
|
||||
¶ms.edit,
|
||||
);
|
||||
|
||||
Ok(json!(lsp::ApplyWorkspaceEditResponse {
|
||||
applied: true,
|
||||
failure_reason: None,
|
||||
failed_change: None,
|
||||
}))
|
||||
}
|
||||
MethodCall::WorkspaceFolders => {
|
||||
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;
|
||||
}
|
||||
};
|
||||
self.editor.language_servers.get_by_id(server_id).unwrap();
|
||||
|
||||
tokio::spawn(language_server.reply(
|
||||
id,
|
||||
Ok(json!(lsp::ApplyWorkspaceEditResponse {
|
||||
applied: true,
|
||||
failure_reason: None,
|
||||
failed_change: None,
|
||||
})),
|
||||
));
|
||||
Ok(json!(language_server.workspace_folders()))
|
||||
}
|
||||
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()
|
||||
@ -668,7 +646,12 @@ pub async fn handle_language_server_message(
|
||||
let doc = self.editor.document_by_path(path)?;
|
||||
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() {
|
||||
for part in section.split('.') {
|
||||
@ -678,9 +661,19 @@ pub async fn handle_language_server_message(
|
||||
Some(config)
|
||||
})
|
||||
.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),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user