diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e6f597de7ff9138b226cd2474353ef8c2ce16ebb..762739325f79d2a3d997901db5388b1374d1ccd9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -19599,6 +19599,7 @@ impl Editor { project.restart_language_servers_for_buffers( multi_buffer.all_buffers().into_iter().collect(), HashSet::default(), + true, cx, ); }); diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index a2722da336b4d52a04a7d6da3c22347a3535bf2b..ecf5d143242e472bb32fd29929b3f29dd6ecc254 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/crates/extension_host/src/extension_store_test.rs @@ -917,7 +917,12 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { // Start a new instance of the language server. project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx) + project.restart_language_servers_for_buffers( + vec![buffer.clone()], + HashSet::default(), + true, + cx, + ) }); cx.executor().run_until_parked(); @@ -958,7 +963,12 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { .await; cx.executor().run_until_parked(); project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx) + project.restart_language_servers_for_buffers( + vec![buffer.clone()], + HashSet::default(), + true, + cx, + ) }); // The extension re-fetches the latest version of the language server. diff --git a/crates/language_tools/src/lsp_button.rs b/crates/language_tools/src/lsp_button.rs index 43b1736223478fe29f45aac0a712fafad1d2dcbe..5aefa29729d7056053793c63048662b52c115be5 100644 --- a/crates/language_tools/src/lsp_button.rs +++ b/crates/language_tools/src/lsp_button.rs @@ -512,6 +512,7 @@ impl LanguageServerState { HashSet::from_iter([LanguageServerSelector::Name( server_name_for_restart.clone(), )]), + true, cx, ); }) diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 7c223419dff7c31126a18ceb663e4f9b74c450c6..8272efa5c4662eb7e3329dd7b19a479d95c4ac02 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -331,7 +331,8 @@ pub struct LocalLspStore { HashMap, HashMap>>, >, restricted_worktrees_tasks: HashMap)>, - language_servers_stopped: bool, + all_language_servers_stopped: bool, + stopped_language_servers: HashSet, buffers_to_refresh_hash_set: HashSet, buffers_to_refresh_queue: VecDeque, @@ -2751,7 +2752,7 @@ impl LocalLspStore { only_register_servers: HashSet, cx: &mut Context, ) { - if self.language_servers_stopped { + if self.all_language_servers_stopped { return; } let buffer = buffer_handle.read(cx); @@ -2813,6 +2814,11 @@ impl LocalLspStore { if reused && server_node.server_id().is_none() { return None; } + if let Some(name) = server_node.name() + && self.stopped_language_servers.contains(&name) + { + return None; + } if !only_register_servers.is_empty() { if let Some(server_id) = server_node.server_id() && !only_register_servers.contains(&LanguageServerSelector::Id(server_id)) @@ -4283,7 +4289,8 @@ 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, + all_language_servers_stopped: false, + stopped_language_servers: HashSet::default(), watched_manifest_filenames: ManifestProvidersStore::global(cx) .manifest_file_names(), }), @@ -5262,11 +5269,15 @@ impl LspStore { .semantic_token_config .update_global_mode(new_global_semantic_tokens_mode) { - let stopped = self + let all_stopped = self .as_local() - .is_some_and(|local| local.language_servers_stopped); - if !stopped { - self.restart_all_language_servers(cx); + .is_some_and(|local| local.all_language_servers_stopped); + if !all_stopped { + // Restart servers without clearing per-server stopped status. + // Individually-stopped servers will be skipped by the guard in + // register_buffer_with_language_servers. + let buffers = self.buffer_store.read(cx).buffers().collect(); + self.restart_language_servers_for_buffers(buffers, HashSet::default(), false, cx); } } @@ -5278,9 +5289,10 @@ impl LspStore { let Some(local) = self.as_local_mut() else { return; }; - if local.language_servers_stopped { + if local.all_language_servers_stopped { return; } + let stopped_language_servers = local.stopped_language_servers.clone(); let mut adapters = BTreeMap::default(); let get_adapter = { let languages = local.languages.clone(); @@ -5363,6 +5375,11 @@ impl LspStore { ) .collect::>(); for node in nodes { + if let Some(name) = node.name() + && stopped_language_servers.contains(&name) + { + continue; + } let server_id = node.server_id_or_init(|disposition| { let path = &disposition.path; let uri = Uri::from_file_path(worktree.read(cx).absolutize(&path.path)); @@ -10523,6 +10540,7 @@ impl LspStore { }) }) .collect(), + true, cx, ); }); @@ -11065,7 +11083,7 @@ impl LspStore { pub fn stop_all_language_servers(&mut self, cx: &mut Context) { if let Some(local) = self.as_local_mut() { - local.language_servers_stopped = true; + local.all_language_servers_stopped = true; } self.shutdown_all_language_servers(cx).detach(); } @@ -11103,16 +11121,18 @@ impl LspStore { pub fn restart_all_language_servers(&mut self, cx: &mut Context) { if let Some(local) = self.as_local_mut() { - local.language_servers_stopped = false; + local.all_language_servers_stopped = false; + local.stopped_language_servers.clear(); } let buffers = self.buffer_store.read(cx).buffers().collect(); - self.restart_language_servers_for_buffers(buffers, HashSet::default(), cx); + self.restart_language_servers_for_buffers(buffers, HashSet::default(), true, cx); } pub fn restart_language_servers_for_buffers( &mut self, buffers: Vec>, only_restart_servers: HashSet, + clear_stopped: bool, cx: &mut Context, ) { if let Some((client, project_id)) = self.upstream_client() { @@ -11146,7 +11166,7 @@ impl LspStore { }); cx.background_spawn(request).detach_and_log_err(cx); } else { - let stop_task = if only_restart_servers.is_empty() { + let (stopped_names, stop_task) = if only_restart_servers.is_empty() { self.stop_local_language_servers_for_buffers(&buffers, HashSet::default(), cx) } else { self.stop_local_language_servers_for_buffers(&[], only_restart_servers.clone(), cx) @@ -11154,6 +11174,13 @@ impl LspStore { cx.spawn(async move |lsp_store, cx| { stop_task.await; lsp_store.update(cx, |lsp_store, cx| { + if clear_stopped { + if let Some(local) = lsp_store.as_local_mut() { + for name in &stopped_names { + local.stopped_language_servers.remove(name); + } + } + } for buffer in buffers { lsp_store.register_buffer_with_language_servers( &buffer, @@ -11208,8 +11235,11 @@ impl LspStore { Ok(()) }) } else { - let task = + let (stopped_names, task) = self.stop_local_language_servers_for_buffers(&buffers, also_stop_servers, cx); + if let Some(local) = self.as_local_mut() { + local.stopped_language_servers.extend(stopped_names); + } cx.background_spawn(async move { task.await; Ok(()) @@ -11222,9 +11252,9 @@ impl LspStore { buffers: &[Entity], also_stop_servers: HashSet, cx: &mut Context, - ) -> Task<()> { + ) -> (HashSet, Task<()>) { let Some(local) = self.as_local_mut() else { - return Task::ready(()); + return (HashSet::default(), Task::ready(())); }; let mut language_server_names_to_stop = BTreeSet::default(); let mut language_servers_to_stop = also_stop_servers @@ -11266,13 +11296,27 @@ impl LspStore { ); } + let stopped_names: HashSet = language_servers_to_stop + .iter() + .filter_map(|id| { + local + .language_server_ids + .iter() + .find(|(_, state)| state.id == *id) + .map(|(seed, _)| seed.name.clone()) + }) + .collect(); + local.lsp_tree.remove_nodes(&language_servers_to_stop); let tasks = language_servers_to_stop .into_iter() .map(|server| self.stop_local_language_server(server, cx)) .collect::>(); - cx.background_spawn(futures::future::join_all(tasks).map(|_| ())) + ( + stopped_names, + cx.background_spawn(futures::future::join_all(tasks).map(|_| ())), + ) } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4d1d4a5da809559a36829b1c171556e9ad4eccd8..9aa119c4ce0bc07c846bc06fb9e064049e2f324e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3852,10 +3852,16 @@ impl Project { &mut self, buffers: Vec>, only_restart_servers: HashSet, + clear_stopped: bool, cx: &mut Context, ) { self.lsp_store.update(cx, |lsp_store, cx| { - lsp_store.restart_language_servers_for_buffers(buffers, only_restart_servers, cx) + lsp_store.restart_language_servers_for_buffers( + buffers, + only_restart_servers, + clear_stopped, + cx, + ) }) } diff --git a/crates/project/tests/integration/project_tests.rs b/crates/project/tests/integration/project_tests.rs index f680ccee78e997064af2647f68d8aa3631fa4bd3..1a604df78667f6c4ce2b55dc54635d94e5af6b7c 100644 --- a/crates/project/tests/integration/project_tests.rs +++ b/crates/project/tests/integration/project_tests.rs @@ -1845,6 +1845,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { project.restart_language_servers_for_buffers( vec![rust_buffer.clone(), json_buffer.clone()], HashSet::default(), + true, cx, ); }); @@ -2884,7 +2885,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC // Restart the server before the diagnostics finish updating. project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer], HashSet::default(), cx); + project.restart_language_servers_for_buffers(vec![buffer], HashSet::default(), true, cx); }); let mut events = cx.events(&project); @@ -3002,7 +3003,12 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp }); project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx); + project.restart_language_servers_for_buffers( + vec![buffer.clone()], + HashSet::default(), + true, + cx, + ); }); // The diagnostics are cleared. @@ -3057,7 +3063,12 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T }); cx.executor().run_until_parked(); project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx); + project.restart_language_servers_for_buffers( + vec![buffer.clone()], + HashSet::default(), + true, + cx, + ); }); let mut fake_server = fake_servers.next().await.unwrap(); @@ -3813,7 +3824,12 @@ async fn test_diagnostic_summaries_cleared_on_server_restart(cx: &mut gpui::Test let mut events = cx.events(&project); project.update(cx, |project, cx| { - project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx); + project.restart_language_servers_for_buffers( + vec![buffer.clone()], + HashSet::default(), + true, + cx, + ); }); cx.executor().run_until_parked();