From 2e98bc17cba04f34b3275f624f509d492fbe72aa Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:45:59 +0100 Subject: [PATCH] lsp: Use available workspace folders in initialize params (#25753) Closes https://github.com/zed-industries/zed/issues/25743 Closes https://github.com/biomejs/biome-zed/issues/73 Release Notes: - Fixed issues with launching Svelte/Biome language servers --- crates/copilot/src/copilot.rs | 1 + crates/languages/src/go.rs | 10 +--------- crates/lsp/src/lsp.rs | 32 ++++++++++++++++++++++++++------ crates/prettier/src/prettier.rs | 1 + crates/project/src/lsp_store.rs | 9 +++++---- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index ff54ce1cef..1bc2de21f8 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -475,6 +475,7 @@ impl Copilot { binary, root_path, None, + Default::default(), cx.clone(), )?; diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index 459ea6987a..94815a9951 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -5,7 +5,7 @@ use futures::StreamExt; use gpui::{App, AsyncApp, Task}; use http_client::github::latest_github_release; pub use language::*; -use lsp::{InitializeParams, LanguageServerBinary, LanguageServerName}; +use lsp::{LanguageServerBinary, LanguageServerName}; use project::Fs; use regex::Regex; use serde_json::json; @@ -373,14 +373,6 @@ impl super::LspAdapter for GoLspAdapter { filter_range, }) } - fn prepare_initialize_params( - &self, - mut original: InitializeParams, - ) -> Result { - #[allow(deprecated)] - let _ = original.root_uri.take(); - Ok(original) - } } fn parse_version_output(output: &Output) -> Result<&str> { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index fb2ae93799..e646ada627 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -329,6 +329,7 @@ impl lsp_types::notification::Notification for ServerStatus { impl LanguageServer { /// Starts a language server process. + #[allow(clippy::too_many_arguments)] pub fn new( stderr_capture: Arc>>, server_id: LanguageServerId, @@ -336,6 +337,7 @@ impl LanguageServer { binary: LanguageServerBinary, root_path: &Path, code_action_kinds: Option>, + workspace_folders: Arc>>, cx: AsyncApp, ) -> Result { let working_dir = if root_path.is_dir() { @@ -383,6 +385,7 @@ impl LanguageServer { code_action_kinds, binary, root_uri, + workspace_folders, cx, move |notification| { log::info!( @@ -409,6 +412,7 @@ impl LanguageServer { code_action_kinds: Option>, binary: LanguageServerBinary, root_uri: Url, + workspace_folders: Arc>>, cx: AsyncApp, on_unhandled_notification: F, ) -> Self @@ -491,7 +495,7 @@ impl LanguageServer { io_tasks: Mutex::new(Some((input_task, output_task))), output_done_rx: Mutex::new(Some(output_done_rx)), server: Arc::new(Mutex::new(server)), - workspace_folders: Default::default(), + workspace_folders, root_uri, } } @@ -617,6 +621,16 @@ impl LanguageServer { } pub fn default_initialize_params(&self, cx: &App) -> InitializeParams { + let workspace_folders = self + .workspace_folders + .lock() + .iter() + .cloned() + .map(|uri| WorkspaceFolder { + name: Default::default(), + uri, + }) + .collect::>(); #[allow(deprecated)] InitializeParams { process_id: None, @@ -791,7 +805,7 @@ impl LanguageServer { }), }, trace: None, - workspace_folders: Some(vec![]), + workspace_folders: Some(workspace_folders), client_info: release_channel::ReleaseChannel::try_global(cx).map(|release_channel| { ClientInfo { name: release_channel.display_name().to_string(), @@ -1260,24 +1274,27 @@ impl LanguageServer { } pub fn set_workspace_folders(&self, folders: BTreeSet) { let mut workspace_folders = self.workspace_folders.lock(); + + let old_workspace_folders = std::mem::take(&mut *workspace_folders); let added: Vec<_> = folders - .iter() + .difference(&old_workspace_folders) .map(|uri| WorkspaceFolder { uri: uri.clone(), name: String::default(), }) .collect(); - let removed: Vec<_> = std::mem::replace(&mut *workspace_folders, folders) - .into_iter() + let removed: Vec<_> = old_workspace_folders + .difference(&folders) .map(|uri| WorkspaceFolder { uri: uri.clone(), name: String::default(), }) .collect(); let should_notify = !added.is_empty() || !removed.is_empty(); - if should_notify { + *workspace_folders = folders; + drop(workspace_folders); let params = DidChangeWorkspaceFoldersParams { event: WorkspaceFoldersChangeEvent { added, removed }, }; @@ -1392,6 +1409,7 @@ impl FakeLanguageServer { let server_name = LanguageServerName(name.clone().into()); let process_name = Arc::from(name.as_str()); let root = Self::root_path(); + let workspace_folders: Arc>> = Default::default(); let mut server = LanguageServer::new_internal( server_id, server_name.clone(), @@ -1403,6 +1421,7 @@ impl FakeLanguageServer { None, binary.clone(), root, + workspace_folders.clone(), cx.clone(), |_| {}, ); @@ -1421,6 +1440,7 @@ impl FakeLanguageServer { None, binary, Self::root_path(), + workspace_folders, cx.clone(), move |msg| { notifications_tx diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 40026b0c4e..836f5df9cc 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -279,6 +279,7 @@ impl Prettier { server_binary, &prettier_dir, None, + Default::default(), cx.clone(), ) .context("prettier server creation")?; diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 806350bb36..7c13a21c6f 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -206,14 +206,14 @@ impl LocalLspStore { ); let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx); - + let pending_workspace_folders: Arc>> = Default::default(); let pending_server = cx.spawn({ let adapter = adapter.clone(); let server_name = adapter.name.clone(); let stderr_capture = stderr_capture.clone(); #[cfg(any(test, feature = "test-support"))] let lsp_store = self.weak.clone(); - + let pending_workspace_folders = pending_workspace_folders.clone(); move |cx| async move { let binary = binary.await?; #[cfg(any(test, feature = "test-support"))] @@ -239,12 +239,12 @@ impl LocalLspStore { binary, &root_path, adapter.code_action_kinds(), + pending_workspace_folders, cx, ) } }); - let pending_workspace_folders: Arc>> = Default::default(); let startup = { let server_name = adapter.name.0.clone(); let delegate = delegate as Arc; @@ -7551,10 +7551,11 @@ impl LspStore { // Update language_servers collection with Running variant of LanguageServerState // indicating that the server is up and running and ready + let workspace_folders = workspace_folders.lock().clone(); local.language_servers.insert( server_id, LanguageServerState::running( - workspace_folders.lock().clone(), + workspace_folders, adapter.clone(), language_server.clone(), None,