Prevent auto-restart of language servers after Stop All Servers

Lukas Wirth created

When the user clicks Stop All Servers, set a flag that suppresses
automatic language server startup. The flag is cleared when Restart
All Servers is hit. This guards the three auto-start paths:

- Buffer registration with language servers (new buffers, file path
  changes, language detection, language registry reloads)
- Server tree refresh (workspace config / manifest changes)
- Settings-triggered restarts (semantic token mode changes)

Change summary

crates/project/src/lsp_store.rs | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)

Detailed changes

crates/project/src/lsp_store.rs 🔗

@@ -331,6 +331,7 @@ pub struct LocalLspStore {
         HashMap<Option<SharedString>, HashMap<PathBuf, Option<SharedString>>>,
     >,
     restricted_worktrees_tasks: HashMap<WorktreeId, (Subscription, watch::Receiver<bool>)>,
+    language_servers_stopped: bool,
 
     buffers_to_refresh_hash_set: HashSet<BufferId>,
     buffers_to_refresh_queue: VecDeque<BufferId>,
@@ -2750,6 +2751,9 @@ impl LocalLspStore {
         only_register_servers: HashSet<LanguageServerSelector>,
         cx: &mut Context<LspStore>,
     ) {
+        if self.language_servers_stopped {
+            return;
+        }
         let buffer = buffer_handle.read(cx);
         let buffer_id = buffer.remote_id();
 
@@ -4279,6 +4283,7 @@ impl LspStore {
                 buffer_pull_diagnostics_result_ids: HashMap::default(),
                 workspace_pull_diagnostics_result_ids: HashMap::default(),
                 restricted_worktrees_tasks: HashMap::default(),
+                language_servers_stopped: false,
                 watched_manifest_filenames: ManifestProvidersStore::global(cx)
                     .manifest_file_names(),
             }),
@@ -5257,7 +5262,12 @@ impl LspStore {
             .semantic_token_config
             .update_global_mode(new_global_semantic_tokens_mode)
         {
-            self.restart_all_language_servers(cx);
+            let stopped = self
+                .as_local()
+                .is_some_and(|local| local.language_servers_stopped);
+            if !stopped {
+                self.restart_all_language_servers(cx);
+            }
         }
 
         cx.notify();
@@ -5268,6 +5278,9 @@ impl LspStore {
         let Some(local) = self.as_local_mut() else {
             return;
         };
+        if local.language_servers_stopped {
+            return;
+        }
         let mut adapters = BTreeMap::default();
         let get_adapter = {
             let languages = local.languages.clone();
@@ -11051,6 +11064,9 @@ impl LspStore {
     }
 
     pub fn stop_all_language_servers(&mut self, cx: &mut Context<Self>) {
+        if let Some(local) = self.as_local_mut() {
+            local.language_servers_stopped = true;
+        }
         self.shutdown_all_language_servers(cx).detach();
     }
 
@@ -11086,6 +11102,9 @@ impl LspStore {
     }
 
     pub fn restart_all_language_servers(&mut self, cx: &mut Context<Self>) {
+        if let Some(local) = self.as_local_mut() {
+            local.language_servers_stopped = false;
+        }
         let buffers = self.buffer_store.read(cx).buffers().collect();
         self.restart_language_servers_for_buffers(buffers, HashSet::default(), cx);
     }