From 38fc4eaf4445a8c1e5f015c281e50b4dd6c0c9c6 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 12 Jan 2026 14:09:43 +0200 Subject: [PATCH] Properly notify all language server worktree trust listeners (#46613) 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 --- 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 0a5407a7b9bcda80da13a08a7d40a75454f53e0f..fa15c3bd06b6c5bf25a8c6059dcee4f92e8075c0 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(); @@ -616,7 +627,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 @@ -625,16 +636,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); } @@ -668,16 +686,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 @@ -13706,7 +13731,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 {