From 4292d883b0699c39ac46f3347dbddecc59798578 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 23 Aug 2025 10:59:51 +0200 Subject: [PATCH] Use language server ids for lsp tool --- .../src/activity_indicator.rs | 22 +++-- crates/extension_host/src/extension_host.rs | 5 +- .../src/extension_store_test.rs | 11 ++- crates/extension_host/src/headless_host.rs | 8 +- crates/language/src/language.rs | 70 +++++++++++---- crates/language/src/language_registry.rs | 26 ++++-- .../src/extension_lsp_adapter.rs | 41 ++++----- crates/language_tools/src/lsp_tool.rs | 86 ++++++------------- crates/project/src/lsp_store.rs | 55 ++++++++---- 9 files changed, 186 insertions(+), 138 deletions(-) diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 6641db0805..7d131d8292 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -56,6 +56,7 @@ pub struct ActivityIndicator { #[derive(Debug)] struct ServerStatus { name: LanguageServerName, + id: LanguageServerId, status: LanguageServerStatusUpdate, } @@ -86,11 +87,12 @@ impl ActivityIndicator { let this = cx.new(|cx| { let mut status_events = languages.language_server_binary_statuses(); cx.spawn(async move |this, cx| { - while let Some((name, binary_status)) = status_events.next().await { + while let Some((id, name, binary_status)) = status_events.next().await { this.update(cx, |this: &mut ActivityIndicator, cx| { - this.statuses.retain(|s| s.name != name); + this.statuses.retain(|s| s.id != id); this.statuses.push(ServerStatus { name, + id, status: LanguageServerStatusUpdate::Binary(binary_status), }); cx.notify(); @@ -117,7 +119,13 @@ impl ActivityIndicator { cx.subscribe( &project.read(cx).lsp_store(), |activity_indicator, _, event, cx| { - if let LspStoreEvent::LanguageServerUpdate { name, message, .. } = event { + if let LspStoreEvent::LanguageServerUpdate { + language_server_id, + name, + message, + .. + } = event + { if let proto::update_language_server::Variant::StatusUpdate(status_update) = message { @@ -180,9 +188,11 @@ impl ActivityIndicator { }; activity_indicator.statuses.retain(|s| s.name != name); - activity_indicator - .statuses - .push(ServerStatus { name, status }); + activity_indicator.statuses.push(ServerStatus { + name, + id: *language_server_id, + status, + }); } cx.notify() } diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index fde0aeac94..979cd8d35b 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -1361,11 +1361,12 @@ impl ExtensionStore { for (manifest, wasm_extension) in &wasm_extensions { let extension = Arc::new(wasm_extension.clone()); - for (language_server_id, language_server_config) in &manifest.language_servers { + for (language_server_name, language_server_config) in &manifest.language_servers + { for language in language_server_config.languages() { this.proxy.register_language_server( extension.clone(), - language_server_id.clone(), + language_server_name.clone(), language.clone(), ); } diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index 347a610439..b6ffa25648 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/crates/extension_host/src/extension_store_test.rs @@ -12,7 +12,7 @@ use gpui::{AppContext as _, SemanticVersion, TestAppContext}; use http_client::{FakeHttpClient, Response}; use language::{BinaryStatus, LanguageMatcher, LanguageName, LanguageRegistry}; use language_extension::LspAccess; -use lsp::LanguageServerName; +use lsp::{LanguageServerId, LanguageServerName}; use node_runtime::NodeRuntime; use parking_lot::Mutex; use project::{DEFAULT_COMPLETION_CONTEXT, Project}; @@ -737,18 +737,25 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { ], [ ( + LanguageServerId(0), LanguageServerName::new_static("gleam"), BinaryStatus::Starting ), ( + LanguageServerId(0), LanguageServerName::new_static("gleam"), BinaryStatus::CheckingForUpdate ), ( + LanguageServerId(0), LanguageServerName::new_static("gleam"), BinaryStatus::Downloading ), - (LanguageServerName::new_static("gleam"), BinaryStatus::None) + ( + LanguageServerId(0), + LanguageServerName::new_static("gleam"), + BinaryStatus::None + ) ] ); diff --git a/crates/extension_host/src/headless_host.rs b/crates/extension_host/src/headless_host.rs index a6305118cd..7862db75f2 100644 --- a/crates/extension_host/src/headless_host.rs +++ b/crates/extension_host/src/headless_host.rs @@ -177,21 +177,21 @@ impl HeadlessExtensionStore { let wasm_extension: Arc = Arc::new(WasmExtension::load(&extension_dir, &manifest, wasm_host.clone(), cx).await?); - for (language_server_id, language_server_config) in &manifest.language_servers { + for (language_server_name, language_server_config) in &manifest.language_servers { for language in language_server_config.languages() { this.update(cx, |this, _cx| { this.loaded_language_servers .entry(manifest.id.clone()) .or_default() - .push((language_server_id.clone(), language.clone())); + .push((language_server_name.clone(), language.clone())); this.proxy.register_language_server( wasm_extension.clone(), - language_server_id.clone(), + language_server_name.clone(), language.clone(), ); })?; } - log::info!("Loaded language server: {}", language_server_id); + log::info!("Loaded language server: {}", language_server_name); } for (debug_adapter, meta) in &manifest.debug_adapters { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 7ae77c9141..3bdc8bf68f 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -214,12 +214,20 @@ impl CachedLspAdapter { delegate: Arc, toolchains: Option, binary_options: LanguageServerBinaryOptions, + server_id: LanguageServerId, cx: &mut AsyncApp, ) -> Result { let cached_binary = self.cached_binary.lock().await; self.adapter .clone() - .get_language_server_command(delegate, toolchains, binary_options, cached_binary, cx) + .get_language_server_command( + delegate, + toolchains, + binary_options, + cached_binary, + server_id, + cx, + ) .await } @@ -291,7 +299,12 @@ pub trait LspAdapterDelegate: Send + Sync { fn http_client(&self) -> Arc; fn worktree_id(&self) -> WorktreeId; fn worktree_root_path(&self) -> &Path; - fn update_status(&self, language: LanguageServerName, status: BinaryStatus); + fn update_status( + &self, + language: LanguageServerId, + server_name: LanguageServerName, + status: BinaryStatus, + ); fn registered_lsp_adapters(&self) -> Vec>; async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option>; @@ -315,6 +328,7 @@ pub trait LspAdapter: 'static + Send + Sync { toolchains: Option, binary_options: LanguageServerBinaryOptions, mut cached_binary: futures::lock::MutexGuard<'a, Option>, + server_id: LanguageServerId, cx: &'a mut AsyncApp, ) -> Pin>>> { async move { @@ -330,27 +344,41 @@ pub trait LspAdapter: 'static + Send + Sync { // because we don't want to download and overwrite our global one // for each worktree we might have open. if binary_options.allow_path_lookup - && let Some(binary) = self.check_if_user_installed(delegate.as_ref(), toolchains, cx).await { - log::debug!( - "found user-installed language server for {}. path: {:?}, arguments: {:?}", - self.name().0, - binary.path, - binary.arguments - ); - return Ok(binary); - } + && let Some(binary) = self + .check_if_user_installed(delegate.as_ref(), toolchains, cx) + .await + { + log::debug!( + "found user-installed language server for {}. path: {:?}, arguments: {:?}", + self.name().0, + binary.path, + binary.arguments + ); + return Ok(binary); + } - anyhow::ensure!(binary_options.allow_binary_download, "downloading language servers disabled"); + anyhow::ensure!( + binary_options.allow_binary_download, + "downloading language servers disabled" + ); if let Some(cached_binary) = cached_binary.as_ref() { return Ok(cached_binary.clone()); } - let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await else { + let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await + else { anyhow::bail!("no language server download dir defined") }; - let mut binary = try_fetch_server_binary(self.as_ref(), &delegate, container_dir.to_path_buf(), cx).await; + let mut binary = try_fetch_server_binary( + self.as_ref(), + &delegate, + container_dir.to_path_buf(), + server_id, + cx, + ) + .await; if let Err(error) = binary.as_ref() { if let Some(prev_downloaded_binary) = self @@ -358,7 +386,8 @@ pub trait LspAdapter: 'static + Send + Sync { .await { log::info!( - "failed to fetch newest version of language server {:?}. error: {:?}, falling back to using {:?}", + "failed to fetch newest version of language server {:?}. \ + error: {:?}, falling back to using {:?}", self.name(), error, prev_downloaded_binary.path @@ -366,6 +395,7 @@ pub trait LspAdapter: 'static + Send + Sync { binary = Ok(prev_downloaded_binary); } else { delegate.update_status( + server_id, self.name(), BinaryStatus::Failed { error: format!("{error:?}"), @@ -594,6 +624,7 @@ async fn try_fetch_server_binary adapter: &L, delegate: &Arc, container_dir: PathBuf, + server_id: LanguageServerId, cx: &mut AsyncApp, ) -> Result { if let Some(task) = adapter.will_fetch_server(delegate, cx) { @@ -602,7 +633,7 @@ async fn try_fetch_server_binary let name = adapter.name(); log::debug!("fetching latest version of language server {:?}", name.0); - delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate); + delegate.update_status(server_id, adapter.name(), BinaryStatus::CheckingForUpdate); let latest_version = adapter .fetch_latest_server_version(delegate.as_ref()) @@ -613,16 +644,16 @@ async fn try_fetch_server_binary .await { log::debug!("language server {:?} is already installed", name.0); - delegate.update_status(name.clone(), BinaryStatus::None); + delegate.update_status(server_id, adapter.name(), BinaryStatus::None); Ok(binary) } else { log::info!("downloading language server {:?}", name.0); - delegate.update_status(adapter.name(), BinaryStatus::Downloading); + delegate.update_status(server_id, adapter.name(), BinaryStatus::Downloading); let binary = adapter .fetch_server_binary(latest_version, container_dir, delegate.as_ref()) .await; - delegate.update_status(name.clone(), BinaryStatus::None); + delegate.update_status(server_id, adapter.name(), BinaryStatus::None); binary } } @@ -2197,6 +2228,7 @@ impl LspAdapter for FakeLspAdapter { _: Option, _: LanguageServerBinaryOptions, _: futures::lock::MutexGuard<'a, Option>, + _: LanguageServerId, _: &'a mut AsyncApp, ) -> Pin>>> { async move { Ok(self.language_server_binary.clone()) }.boxed_local() diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 4f07240e44..29ad524eeb 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -252,7 +252,9 @@ pub struct LanguageQueries { #[derive(Clone, Default)] struct ServerStatusSender { - txs: Arc>>>, + txs: Arc< + Mutex>>, + >, } pub struct LoadedLanguage { @@ -1077,8 +1079,13 @@ impl LanguageRegistry { self.state.read().all_lsp_adapters.get(name).cloned() } - pub fn update_lsp_binary_status(&self, server_name: LanguageServerName, status: BinaryStatus) { - self.lsp_binary_status_tx.send(server_name, status); + pub fn update_lsp_binary_status( + &self, + server_id: LanguageServerId, + name: LanguageServerName, + status: BinaryStatus, + ) { + self.lsp_binary_status_tx.send(server_id, name, status); } pub fn next_language_server_id(&self) -> LanguageServerId { @@ -1133,7 +1140,7 @@ impl LanguageRegistry { pub fn language_server_binary_statuses( &self, - ) -> mpsc::UnboundedReceiver<(LanguageServerName, BinaryStatus)> { + ) -> mpsc::UnboundedReceiver<(LanguageServerId, LanguageServerName, BinaryStatus)> { self.lsp_binary_status_tx.subscribe() } @@ -1247,14 +1254,19 @@ impl LanguageRegistryState { } impl ServerStatusSender { - fn subscribe(&self) -> mpsc::UnboundedReceiver<(LanguageServerName, BinaryStatus)> { + fn subscribe( + &self, + ) -> mpsc::UnboundedReceiver<(LanguageServerId, LanguageServerName, BinaryStatus)> { let (tx, rx) = mpsc::unbounded(); self.txs.lock().push(tx); rx } - fn send(&self, name: LanguageServerName, status: BinaryStatus) { + fn send(&self, id: LanguageServerId, name: LanguageServerName, status: BinaryStatus) { let mut txs = self.txs.lock(); - txs.retain(|tx| tx.unbounded_send((name.clone(), status.clone())).is_ok()); + txs.retain(|tx| { + tx.unbounded_send((id, name.clone(), status.clone())) + .is_ok() + }); } } diff --git a/crates/language_extension/src/extension_lsp_adapter.rs b/crates/language_extension/src/extension_lsp_adapter.rs index e465a8dd0a..fbd09a53d9 100644 --- a/crates/language_extension/src/extension_lsp_adapter.rs +++ b/crates/language_extension/src/extension_lsp_adapter.rs @@ -16,8 +16,8 @@ use language::{ Toolchain, }; use lsp::{ - CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName, - LanguageServerSelector, + CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId, + LanguageServerName, LanguageServerSelector, }; use serde::Serialize; use serde_json::Value; @@ -58,14 +58,14 @@ impl ExtensionLanguageServerProxy for LanguageServerRegistryProxy { fn register_language_server( &self, extension: Arc, - language_server_id: LanguageServerName, + language_server_name: LanguageServerName, language: LanguageName, ) { self.language_registry.register_lsp_adapter( language.clone(), Arc::new(ExtensionLspAdapter::new( extension, - language_server_id, + language_server_name, language, )), ); @@ -122,29 +122,29 @@ impl ExtensionLanguageServerProxy for LanguageServerRegistryProxy { fn update_language_server_status( &self, - language_server_id: LanguageServerName, + language_server_name: LanguageServerName, status: BinaryStatus, ) { - self.language_registry - .update_lsp_binary_status(language_server_id, status); + // self.language_registry + // .update_lsp_binary_status(language_server_name, status); } } struct ExtensionLspAdapter { extension: Arc, - language_server_id: LanguageServerName, + language_server_name: LanguageServerName, language_name: LanguageName, } impl ExtensionLspAdapter { fn new( extension: Arc, - language_server_id: LanguageServerName, + language_server_name: LanguageServerName, language_name: LanguageName, ) -> Self { Self { extension, - language_server_id, + language_server_name, language_name, } } @@ -153,7 +153,7 @@ impl ExtensionLspAdapter { #[async_trait(?Send)] impl LspAdapter for ExtensionLspAdapter { fn name(&self) -> LanguageServerName { - self.language_server_id.clone() + self.language_server_name.clone() } fn get_language_server_command<'a>( @@ -162,6 +162,7 @@ impl LspAdapter for ExtensionLspAdapter { _: Option, _: LanguageServerBinaryOptions, _: futures::lock::MutexGuard<'a, Option>, + _: LanguageServerId, _: &'a mut AsyncApp, ) -> Pin>>> { async move { @@ -169,7 +170,7 @@ impl LspAdapter for ExtensionLspAdapter { let command = self .extension .language_server_command( - self.language_server_id.clone(), + self.language_server_name.clone(), self.language_name.clone(), delegate, ) @@ -230,7 +231,7 @@ impl LspAdapter for ExtensionLspAdapter { .extension .manifest() .language_servers - .get(&self.language_server_id) + .get(&self.language_server_name) .and_then(|server| server.code_action_kinds.clone()); code_action_kinds.or(Some(vec![ @@ -256,7 +257,7 @@ impl LspAdapter for ExtensionLspAdapter { self.extension .manifest() .language_servers - .get(&self.language_server_id) + .get(&self.language_server_name) .map(|server| server.language_ids.clone()) .unwrap_or_default() } @@ -270,7 +271,7 @@ impl LspAdapter for ExtensionLspAdapter { let json_options = self .extension .language_server_initialization_options( - self.language_server_id.clone(), + self.language_server_name.clone(), self.language_name.clone(), delegate, ) @@ -294,7 +295,7 @@ impl LspAdapter for ExtensionLspAdapter { let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let json_options: Option = self .extension - .language_server_workspace_configuration(self.language_server_id.clone(), delegate) + .language_server_workspace_configuration(self.language_server_name.clone(), delegate) .await?; Ok(if let Some(json_options) = json_options { serde_json::from_str(&json_options).with_context(|| { @@ -315,7 +316,7 @@ impl LspAdapter for ExtensionLspAdapter { let json_options: Option = self .extension .language_server_additional_initialization_options( - self.language_server_id.clone(), + self.language_server_name.clone(), target_language_server_id.clone(), delegate, ) @@ -343,7 +344,7 @@ impl LspAdapter for ExtensionLspAdapter { let json_options: Option = self .extension .language_server_additional_workspace_configuration( - self.language_server_id.clone(), + self.language_server_name.clone(), target_language_server_id.clone(), delegate, ) @@ -370,7 +371,7 @@ impl LspAdapter for ExtensionLspAdapter { let labels = self .extension - .labels_for_completions(self.language_server_id.clone(), completions) + .labels_for_completions(self.language_server_name.clone(), completions) .await?; Ok(labels_from_extension(labels, language)) @@ -392,7 +393,7 @@ impl LspAdapter for ExtensionLspAdapter { let labels = self .extension - .labels_for_symbols(self.language_server_id.clone(), symbols) + .labels_for_symbols(self.language_server_name.clone(), symbols) .await?; Ok(labels_from_extension(labels, language)) diff --git a/crates/language_tools/src/lsp_tool.rs b/crates/language_tools/src/lsp_tool.rs index dd3e80212f..18ba583163 100644 --- a/crates/language_tools/src/lsp_tool.rs +++ b/crates/language_tools/src/lsp_tool.rs @@ -65,7 +65,7 @@ impl std::fmt::Debug for ActiveEditor { #[derive(Debug, Default, Clone)] struct LanguageServers { health_statuses: HashMap, - binary_statuses: HashMap, + binary_statuses: HashMap, servers_per_buffer_abs_path: HashMap, } @@ -85,6 +85,7 @@ struct LanguageServerHealthStatus { struct LanguageServerBinaryStatus { status: BinaryStatus, message: Option, + name: LanguageServerName, } #[derive(Debug)] @@ -370,17 +371,19 @@ impl LanguageServers { binary_status: BinaryStatus, message: Option<&str>, name: LanguageServerName, + id: LanguageServerId, ) { let binary_status_message = message.map(SharedString::new); if matches!( binary_status, BinaryStatus::Stopped | BinaryStatus::Failed { .. } ) { - self.health_statuses.retain(|_, server| server.name != name); + self.health_statuses.remove(&id); } self.binary_statuses.insert( - name, + id, LanguageServerBinaryStatus { + name, status: binary_status, message: binary_status_message, }, @@ -583,18 +586,16 @@ impl LspTool { proto::ServerBinaryStatus::Starting => BinaryStatus::Starting, proto::ServerBinaryStatus::Stopping => BinaryStatus::Stopping, proto::ServerBinaryStatus::Stopped => BinaryStatus::Stopped, - proto::ServerBinaryStatus::Failed => { - let Some(error) = status_update.message.clone() else { - return; - }; - BinaryStatus::Failed { error } - } + proto::ServerBinaryStatus::Failed => BinaryStatus::Failed { + error: status_update.message.clone().unwrap_or_default(), + }, }; self.server_state.update(cx, |state, _| { state.language_servers.update_binary_status( binary_status, status_update.message.as_deref(), name.clone(), + *language_server_id, ); }); updated = true; @@ -684,27 +685,16 @@ impl LspTool { }) }) .collect::>(); - let mut server_ids_to_worktrees = HashMap::>::default(); - let mut server_names_to_worktrees = HashMap::< - LanguageServerName, - HashSet<(Entity, LanguageServerId)>, - >::default(); for servers_for_path in state.language_servers.servers_per_buffer_abs_path.values() { if let Some(worktree) = servers_for_path .worktree .as_ref() .and_then(|worktree| worktree.upgrade()) { - for (server_id, server_name) in &servers_for_path.servers { + for (server_id, _) in &servers_for_path.servers { server_ids_to_worktrees.insert(*server_id, worktree.clone()); - if let Some(server_name) = server_name { - server_names_to_worktrees - .entry(server_name.clone()) - .or_default() - .insert((worktree.clone(), *server_id)); - } } } } @@ -714,19 +704,12 @@ impl LspTool { let mut servers_with_health_checks = HashSet::default(); for (server_id, health) in &state.language_servers.health_statuses { - let worktree = server_ids_to_worktrees.get(server_id).or_else(|| { - let worktrees = server_names_to_worktrees.get(&health.name)?; - worktrees - .iter() - .find(|(worktree, _)| active_worktrees.contains(worktree)) - .or_else(|| worktrees.iter().next()) - .map(|(worktree, _)| worktree) - }); - servers_with_health_checks.insert(&health.name); + let worktree = server_ids_to_worktrees.get(server_id); + servers_with_health_checks.insert(*server_id); let worktree_name = worktree.map(|worktree| SharedString::new(worktree.read(cx).root_name())); - let binary_status = state.language_servers.binary_statuses.get(&health.name); + let binary_status = state.language_servers.binary_statuses.get(server_id); let server_data = ServerData::WithHealthCheck { server_id: *server_id, health, @@ -743,11 +726,11 @@ impl LspTool { let mut can_stop_all = !state.language_servers.health_statuses.is_empty(); let mut can_restart_all = state.language_servers.health_statuses.is_empty(); - for (server_name, binary_status) in state + for (server_id, binary_status) in state .language_servers .binary_statuses .iter() - .filter(|(name, _)| !servers_with_health_checks.contains(name)) + .filter(|&(id, _)| !servers_with_health_checks.contains(id)) { match binary_status.status { BinaryStatus::None => { @@ -774,40 +757,25 @@ impl LspTool { BinaryStatus::Failed { .. } => {} } - match server_names_to_worktrees.get(server_name) { - Some(worktrees_for_name) => { - match worktrees_for_name - .iter() - .find(|(worktree, _)| active_worktrees.contains(worktree)) - .or_else(|| worktrees_for_name.iter().next()) - { - Some((worktree, server_id)) => { - let worktree_name = - SharedString::new(worktree.read(cx).root_name()); - servers_per_worktree - .entry(worktree_name.clone()) - .or_default() - .push(ServerData::WithBinaryStatus { - server_name, - binary_status, - server_id: Some(*server_id), - }); - } - None => servers_without_worktree.push(ServerData::WithBinaryStatus { + let server_name = &binary_status.name; + match server_ids_to_worktrees.get(server_id) { + Some(worktree) if active_worktrees.contains(worktree) => { + let worktree_name = SharedString::new(worktree.read(cx).root_name()); + servers_per_worktree.entry(worktree_name).or_default().push( + ServerData::WithBinaryStatus { server_name, binary_status, - server_id: None, - }), - } + server_id: Some(*server_id), + }, + ); } - None => servers_without_worktree.push(ServerData::WithBinaryStatus { + _ => servers_without_worktree.push(ServerData::WithBinaryStatus { server_name, binary_status, - server_id: None, + server_id: Some(*server_id), }), } } - let mut new_lsp_items = Vec::with_capacity(servers_per_worktree.len() + servers_without_worktree.len() + 2); for (worktree_name, worktree_servers) in servers_per_worktree { diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index fb1fae3736..ac1c532187 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -308,6 +308,7 @@ impl LocalLspStore { toolchain.clone(), delegate.clone(), true, + server_id, cx, ); let pending_workspace_folders: Arc>> = Default::default(); @@ -351,11 +352,11 @@ impl LocalLspStore { } }); + let server_name = adapter.name.clone(); let startup = { - let server_name = adapter.name.0.clone(); + let server_name = server_name.clone(); let delegate = delegate as Arc; let key = key.clone(); - let adapter = adapter.clone(); let lsp_store = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); let fs = self.fs.clone(); @@ -460,8 +461,10 @@ impl LocalLspStore { Err(err) => { let log = stderr_capture.lock().take().unwrap_or_default(); + log::error!("Failed to start language server {server_name:?}: {err:?}"); delegate.update_status( - adapter.name(), + server_id, + server_name, BinaryStatus::Failed { error: if log.is_empty() { format!("{err:#}") @@ -470,7 +473,6 @@ impl LocalLspStore { }, }, ); - log::error!("Failed to start language server {server_name:?}: {err:?}"); if !log.is_empty() { log::error!("server stderr: {log}"); } @@ -485,7 +487,7 @@ impl LocalLspStore { }; self.languages - .update_lsp_binary_status(adapter.name(), BinaryStatus::Starting); + .update_lsp_binary_status(server_id, server_name, BinaryStatus::Starting); self.language_servers.insert(server_id, state); self.language_server_ids @@ -504,6 +506,7 @@ impl LocalLspStore { toolchain: Option, delegate: Arc, allow_binary_download: bool, + server_id: LanguageServerId, cx: &mut App, ) -> Task> { if let Some(settings) = settings.binary.as_ref() @@ -539,10 +542,16 @@ impl LocalLspStore { cx.spawn(async move |cx| { let binary_result = adapter .clone() - .get_language_server_command(delegate.clone(), toolchain, lsp_binary_options, cx) + .get_language_server_command( + delegate.clone(), + toolchain, + lsp_binary_options, + server_id, + cx, + ) .await; - delegate.update_status(adapter.name.clone(), BinaryStatus::None); + delegate.update_status(server_id, adapter.name(), BinaryStatus::None); let mut binary = binary_result?; let mut shell_env = delegate.shell_env().await; @@ -10409,17 +10418,22 @@ impl LspStore { if let Some(name) = name { log::info!("stopping language server {name}"); - self.languages - .update_lsp_binary_status(name.clone(), BinaryStatus::Stopping); + self.languages.update_lsp_binary_status( + server_id, + name.clone(), + BinaryStatus::Stopping, + ); cx.notify(); return cx.spawn(async move |lsp_store, cx| { Self::shutdown_language_server(server_state, name.clone(), cx).await; lsp_store .update(cx, |lsp_store, cx| { - lsp_store - .languages - .update_lsp_binary_status(name, BinaryStatus::Stopped); + lsp_store.languages.update_lsp_binary_status( + server_id, + name.clone(), + BinaryStatus::Stopped, + ); cx.emit(LspStoreEvent::LanguageServerRemoved(server_id)); cx.notify(); }) @@ -10882,7 +10896,7 @@ impl LspStore { ); local .languages - .update_lsp_binary_status(adapter.name(), BinaryStatus::None); + .update_lsp_binary_status(server_id, adapter.name(), BinaryStatus::None); if let Some(file_ops_caps) = language_server .capabilities() .workspace @@ -12197,7 +12211,7 @@ fn subscribe_to_binary_statuses( ) -> Task<()> { let mut server_statuses = languages.language_server_binary_statuses(); cx.spawn(async move |lsp_store, cx| { - while let Some((server_name, binary_status)) = server_statuses.next().await { + while let Some((server_id, server_name, binary_status)) = server_statuses.next().await { if lsp_store .update(cx, |_, cx| { let mut message = None; @@ -12216,9 +12230,7 @@ fn subscribe_to_binary_statuses( } }; cx.emit(LspStoreEvent::LanguageServerUpdate { - // Binary updates are about the binary that might not have any language server id at that point. - // Reuse `LanguageServerUpdate` for them and provide a fake id that won't be used on the receiver side. - language_server_id: LanguageServerId(0), + language_server_id: server_id, name: Some(server_name), message: proto::update_language_server::Variant::StatusUpdate( proto::StatusUpdate { @@ -13154,9 +13166,14 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate { Ok(()) } - fn update_status(&self, server_name: LanguageServerName, status: language::BinaryStatus) { + fn update_status( + &self, + server_id: LanguageServerId, + server_name: LanguageServerName, + status: language::BinaryStatus, + ) { self.language_registry - .update_lsp_binary_status(server_name, status); + .update_lsp_binary_status(server_id, server_name, status); } fn registered_lsp_adapters(&self) -> Vec> {