on master help sign in
lsp: Implement support for workspace_folders (currently just one)

Refs #1898

commit8adf0c1b3a7a2173c80a86da5d435288bab456de authorBlaž Hrastnik <blaz@mxxn.io> dateFri 1 Apr 2022, 11:20 (5 years ago) parent236c6b7
3 files changed+72−50
helix-lsp/src/client.rs+40−13 helix-lsp/src/lib.rs+3−1 helix-term/src/application.rs+29−36
helix-lsp/src/client.rs+40−13
@@ -31,7 +31,9 @@ pub struct Client {
3131 pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
3232 offset_encoding: OffsetEncoding,
3333 config: Option<Value>,
34− root_markers: Vec<String>,
34+ root_path: Option<std::path::PathBuf>,
35+ root_uri: Option<lsp::Url>,
36+ workspace_folders: Vec<lsp::WorkspaceFolder>,
3537}
3638
3739impl Client {
@@ -40,7 +42,7 @@ impl Client {
4042 cmd: &str,
4143 args: &[String],
4244 config: Option<Value>,
43− root_markers: Vec<String>,
45+ root_markers: &[String],
4446 id: usize,
4547 ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
4648 // Resolve path to the binary
@@ -65,6 +67,27 @@ impl Client {
6567 let (server_rx, server_tx, initialize_notify) =
6668 Transport::start(reader, writer, stderr, id);
6769
70+ let root_path = find_root(None, root_markers);
71+
72+ let root_uri = root_path
73+ .clone()
74+ .and_then(|root| lsp::Url::from_file_path(root).ok());
75+
76+ // TODO: support multiple workspace folders
77+ let workspace_folders = root_uri
78+ .clone()
79+ .map(|root| {
80+ vec![lsp::WorkspaceFolder {
81+ name: root
82+ .path_segments()
83+ .and_then(|segments| segments.last())
84+ .map(|basename| basename.to_string())
85+ .unwrap_or_default(),
86+ uri: root,
87+ }]
88+ })
89+ .unwrap_or_default();
90+
6891 let client = Self {
6992 id,
7093 _process: process,
@@ -73,7 +96,10 @@ impl Client {
7396 capabilities: OnceCell::new(),
7497 offset_encoding: OffsetEncoding::Utf8,
7598 config,
76− root_markers,
99+
100+ root_path,
101+ root_uri,
102+ workspace_folders,
77103 };
78104
79105 Ok((client, server_rx, initialize_notify))
@@ -117,6 +143,10 @@ impl Client {
117143 self.config.as_ref()
118144 }
119145
146+ pub fn workspace_folders(&self) -> &[lsp::WorkspaceFolder] {
147+ &self.workspace_folders
148+ }
149+
120150 /// Execute a RPC request on the language server.
121151 async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
122152 where
@@ -234,13 +264,6 @@ impl Client {
234264 // -------------------------------------------------------------------------------------------
235265
236266 pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
237− // TODO: delay any requests that are triggered prior to initialize
238− let root_path = find_root(None, &self.root_markers);
239−
240− let root_uri = root_path
241− .clone()
242− .and_then(|root| lsp::Url::from_file_path(root).ok());
243−
244267 if self.config.is_some() {
245268 log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
246269 }
@@ -248,10 +271,14 @@ impl Client {
248271 #[allow(deprecated)]
249272 let params = lsp::InitializeParams {
250273 process_id: Some(std::process::id()),
274+ workspace_folders: Some(self.workspace_folders.clone()),
251275 // root_path is obsolete, but some clients like pyright still use it so we specify both.
252276 // clients will prefer _uri if possible
253− root_path: root_path.and_then(|path| path.to_str().map(|path| path.to_owned())),
254− root_uri,
277+ root_path: self
278+ .root_path
279+ .clone()
280+ .and_then(|path| path.to_str().map(|path| path.to_owned())),
281+ root_uri: self.root_uri.clone(),
255282 initialization_options: self.config.clone(),
256283 capabilities: lsp::ClientCapabilities {
257284 workspace: Some(lsp::WorkspaceClientCapabilities {
@@ -259,6 +286,7 @@ impl Client {
259286 did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities {
260287 dynamic_registration: Some(false),
261288 }),
289+ workspace_folders: Some(true),
262290 ..Default::default()
263291 }),
264292 text_document: Some(lsp::TextDocumentClientCapabilities {
@@ -314,7 +342,6 @@ impl Client {
314342 ..Default::default()
315343 },
316344 trace: None,
317− workspace_folders: None,
318345 client_info: None,
319346 locale: None, // TODO
320347 };
helix-lsp/src/lib.rs+3−1
@@ -191,6 +191,7 @@ pub mod util {
191191pub enum MethodCall {
192192 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
193193 ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
194+ WorkspaceFolders,
194195 WorkspaceConfiguration(lsp::ConfigurationParams),
195196}
196197
@@ -210,6 +211,7 @@ impl MethodCall {
210211 .expect("Failed to parse ApplyWorkspaceEdit params");
211212 Self::ApplyWorkspaceEdit(params)
212213 }
214+ lsp::request::WorkspaceFoldersRequest::METHOD => Self::WorkspaceFolders,
213215 lsp::request::WorkspaceConfiguration::METHOD => {
214216 let params: lsp::ConfigurationParams = params
215217 .parse()
@@ -320,7 +322,7 @@ impl Registry {
320322 &config.command,
321323 &config.args,
322324 language_config.config.clone(),
323− language_config.roots.clone(),
325+ &language_config.roots,
324326 id,
325327 )?;
326328 self.incoming.push(UnboundedReceiverStream::new(incoming));
helix-term/src/application.rs+29−36
@@ -601,7 +601,7 @@ impl Application {
601601 }
602602 };
603603
604− match call {
604+ let reply = match call {
605605 MethodCall::WorkDoneProgressCreate(params) => {
606606 self.lsp_progress.create(server_id, params.token);
607607
@@ -613,16 +613,8 @@ impl Application {
613613 if spinner.is_stopped() {
614614 spinner.start();
615615 }
616− let language_server =
617− match self.editor.language_servers.get_by_id(server_id) {
618− Some(language_server) => language_server,
619− None => {
620− warn!("can't find language server with id `{}`", server_id);
621− return;
622− }
623− };
624616
625− tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null)));
617+ Ok(serde_json::Value::Null)
626618 }
627619 MethodCall::ApplyWorkspaceEdit(params) => {
628620 apply_workspace_edit(
@@ -631,33 +623,19 @@ impl Application {
631623 &params.edit,
632624 );
633625
626+ Ok(json!(lsp::ApplyWorkspaceEditResponse {
627+ applied: true,
628+ failure_reason: None,
629+ failed_change: None,
630+ }))
631+ }
632+ MethodCall::WorkspaceFolders => {
634633 let language_server =
635− match self.editor.language_servers.get_by_id(server_id) {
636− Some(language_server) => language_server,
637− None => {
638− warn!("can't find language server with id `{}`", server_id);
639− return;
640− }
641− };
634+ self.editor.language_servers.get_by_id(server_id).unwrap();
642635
643− tokio::spawn(language_server.reply(
644− id,
645− Ok(json!(lsp::ApplyWorkspaceEditResponse {
646− applied: true,
647− failure_reason: None,
648− failed_change: None,
649− })),
650− ));
636+ Ok(json!(language_server.workspace_folders()))
651637 }
652638 MethodCall::WorkspaceConfiguration(params) => {
653− let language_server =
654− match self.editor.language_servers.get_by_id(server_id) {
655− Some(language_server) => language_server,
656− None => {
657− warn!("can't find language server with id `{}`", server_id);
658− return;
659− }
660− };
661639 let result: Vec<_> = params
662640 .items
663641 .iter()
@@ -668,7 +646,12 @@ impl Application {
668646 let doc = self.editor.document_by_path(path)?;
669647 doc.language_config()?.config.as_ref()?
670648 }
671− None => language_server.config()?,
649+ None => self
650+ .editor
651+ .language_servers
652+ .get_by_id(server_id)
653+ .unwrap()
654+ .config()?,
672655 };
673656 if let Some(section) = item.section.as_ref() {
674657 for part in section.split('.') {
@@ -678,9 +661,19 @@ impl Application {
678661 Some(config)
679662 })
680663 .collect();
681− tokio::spawn(language_server.reply(id, Ok(json!(result))));
664+ Ok(json!(result))
682665 }
683− }
666+ };
667+
668+ let language_server = match self.editor.language_servers.get_by_id(server_id) {
669+ Some(language_server) => language_server,
670+ None => {
671+ warn!("can't find language server with id `{}`", server_id);
672+ return;
673+ }
674+ };
675+
676+ tokio::spawn(language_server.reply(id, reply));
684677 }
685678 Call::Invalid { id } => log::error!("LSP invalid method call id={:?}", id),
686679 }