From 4cf8795b586a9d0d3db9ad3441bf73bbfeba4bbd Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:47:28 +0000 Subject: [PATCH] Properly notify all language server worktree trust listeners (#46613) (cherry-pick to preview) (#46614) Cherry-pick of #46613 to preview ---- Closes https://github.com/zed-industries/zed/issues/46590 https://github.com/user-attachments/assets/8464a3b6-9258-4d15-9168-56178bf46437 `use smol::channel::{Receiver, Sender};` do not propagate the value change to all receivers, hence only one language server received the "worktree trusted" message. The fix uses `watch` kind of channels instead, and cleans up its state more eager to rely on the sender drop as another form of notification. Release Notes: - Fixed groups of language servers not starting after worktree trust approval Co-authored-by: Kirill Bulatov --- crates/project/src/lsp_store.rs | 85 +++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 81e6d5291b858bf7c1305ffee6523bfd421f9821..7c7a08365e0a3557d3f6dbc2d2ee073a4377dff9 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -98,7 +98,6 @@ use serde::Serialize; use serde_json::Value; use settings::{Settings, SettingsLocation, SettingsStore}; use sha2::{Digest, Sha256}; -use smol::channel::{Receiver, Sender}; use snippet::Snippet; use std::{ any::TypeId, @@ -300,7 +299,7 @@ pub struct LocalLspStore { LanguageServerId, HashMap, HashMap>>, >, - restricted_worktrees_tasks: HashMap)>, + restricted_worktrees_tasks: HashMap)>, } impl LocalLspStore { @@ -385,7 +384,7 @@ impl LocalLspStore { adapter.name.0 ); - let untrusted_worktree_task = + let wait_until_worktree_trust = TrustedWorktrees::try_get_global(cx).and_then(|trusted_worktrees| { let can_trust = trusted_worktrees.update(cx, |trusted_worktrees, cx| { trusted_worktrees.can_trust(&self.worktree_store, worktree_id, cx) @@ -397,11 +396,23 @@ impl LocalLspStore { match self.restricted_worktrees_tasks.entry(worktree_id) { hash_map::Entry::Occupied(o) => Some(o.get().1.clone()), hash_map::Entry::Vacant(v) => { - let (tx, rx) = smol::channel::bounded::<()>(1); - let subscription = cx.subscribe(&trusted_worktrees, move |_, e, _| { + let (mut tx, rx) = watch::channel::(); + let lsp_store = self.weak.clone(); + let subscription = cx.subscribe(&trusted_worktrees, move |_, e, cx| { if let TrustedWorktreesEvent::Trusted(_, trusted_paths) = e { if trusted_paths.contains(&PathTrust::Worktree(worktree_id)) { - tx.send_blocking(()).ok(); + tx.blocking_send(true).ok(); + lsp_store + .update(cx, |lsp_store, _| { + if let Some(local_lsp_store) = + lsp_store.as_local_mut() + { + local_lsp_store + .restricted_worktrees_tasks + .remove(&worktree_id); + } + }) + .ok(); } } }); @@ -411,7 +422,7 @@ impl LocalLspStore { } } }); - let update_binary_status = untrusted_worktree_task.is_none(); + let update_binary_status = wait_until_worktree_trust.is_none(); let binary = self.get_language_server_binary( worktree_abs_path.clone(), @@ -420,7 +431,7 @@ impl LocalLspStore { toolchain.clone(), delegate.clone(), true, - untrusted_worktree_task, + wait_until_worktree_trust, cx, ); let pending_workspace_folders = Arc::>>::default(); @@ -618,7 +629,7 @@ impl LocalLspStore { toolchain: Option, delegate: Arc, allow_binary_download: bool, - untrusted_worktree_task: Option>, + wait_until_worktree_trust: Option>, cx: &mut App, ) -> Task> { if let Some(settings) = &settings.binary @@ -627,16 +638,23 @@ impl LocalLspStore { let settings = settings.clone(); let languages = self.languages.clone(); return cx.background_spawn(async move { - if let Some(untrusted_worktree_task) = untrusted_worktree_task { - log::info!( - "Waiting for worktree {worktree_abs_path:?} to be trusted, before starting language server {}", - adapter.name(), - ); - untrusted_worktree_task.recv().await.ok(); - log::info!( - "Worktree {worktree_abs_path:?} is trusted, starting language server {}", - adapter.name(), - ); + if let Some(mut wait_until_worktree_trust) = wait_until_worktree_trust { + let already_trusted = *wait_until_worktree_trust.borrow(); + if !already_trusted { + log::info!( + "Waiting for worktree {worktree_abs_path:?} to be trusted, before starting language server {}", + adapter.name(), + ); + while let Some(worktree_trusted) = wait_until_worktree_trust.recv().await { + if worktree_trusted { + break; + } + } + log::info!( + "Worktree {worktree_abs_path:?} is trusted, starting language server {}", + adapter.name(), + ); + } languages .update_lsp_binary_status(adapter.name(), BinaryStatus::Starting); } @@ -670,16 +688,23 @@ impl LocalLspStore { }; cx.spawn(async move |cx| { - if let Some(untrusted_worktree_task) = untrusted_worktree_task { - log::info!( - "Waiting for worktree {worktree_abs_path:?} to be trusted, before starting language server {}", - adapter.name(), - ); - untrusted_worktree_task.recv().await.ok(); - log::info!( - "Worktree {worktree_abs_path:?} is trusted, starting language server {}", - adapter.name(), - ); + if let Some(mut wait_until_worktree_trust) = wait_until_worktree_trust { + let already_trusted = *wait_until_worktree_trust.borrow(); + if !already_trusted { + log::info!( + "Waiting for worktree {worktree_abs_path:?} to be trusted, before starting language server {}", + adapter.name(), + ); + while let Some(worktree_trusted) = wait_until_worktree_trust.recv().await { + if worktree_trusted { + break; + } + } + log::info!( + "Worktree {worktree_abs_path:?} is trusted, starting language server {}", + adapter.name(), + ); + } } let (existing_binary, maybe_download_binary) = adapter @@ -13693,7 +13718,7 @@ pub struct LanguageServerPromptRequest { pub message: String, pub actions: Vec, pub lsp_name: String, - pub(crate) response_channel: Sender, + pub(crate) response_channel: smol::channel::Sender, } impl LanguageServerPromptRequest {