Detailed changes
@@ -19599,6 +19599,7 @@ impl Editor {
project.restart_language_servers_for_buffers(
multi_buffer.all_buffers().into_iter().collect(),
HashSet::default(),
+ true,
cx,
);
});
@@ -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.
@@ -512,6 +512,7 @@ impl LanguageServerState {
HashSet::from_iter([LanguageServerSelector::Name(
server_name_for_restart.clone(),
)]),
+ true,
cx,
);
})
@@ -331,7 +331,8 @@ pub struct LocalLspStore {
HashMap<Option<SharedString>, HashMap<PathBuf, Option<SharedString>>>,
>,
restricted_worktrees_tasks: HashMap<WorktreeId, (Subscription, watch::Receiver<bool>)>,
- language_servers_stopped: bool,
+ all_language_servers_stopped: bool,
+ stopped_language_servers: HashSet<LanguageServerName>,
buffers_to_refresh_hash_set: HashSet<BufferId>,
buffers_to_refresh_queue: VecDeque<BufferId>,
@@ -2751,7 +2752,7 @@ impl LocalLspStore {
only_register_servers: HashSet<LanguageServerSelector>,
cx: &mut Context<LspStore>,
) {
- 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::<Vec<_>>();
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<Self>) {
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<Self>) {
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<Entity<Buffer>>,
only_restart_servers: HashSet<LanguageServerSelector>,
+ clear_stopped: bool,
cx: &mut Context<Self>,
) {
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<Buffer>],
also_stop_servers: HashSet<LanguageServerSelector>,
cx: &mut Context<Self>,
- ) -> Task<()> {
+ ) -> (HashSet<LanguageServerName>, 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<LanguageServerName> = 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::<Vec<_>>();
- 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"))]
@@ -3852,10 +3852,16 @@ impl Project {
&mut self,
buffers: Vec<Entity<Buffer>>,
only_restart_servers: HashSet<LanguageServerSelector>,
+ clear_stopped: bool,
cx: &mut Context<Self>,
) {
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,
+ )
})
}
@@ -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();