Detailed changes
@@ -433,7 +433,7 @@ impl Copilot {
this
}
- fn shutdown_language_server(
+ pub fn shutdown_language_server(
&mut self,
_cx: &mut Context<Self>,
) -> impl Future<Output = ()> + use<> {
@@ -1037,7 +1037,13 @@ impl EditPredictionStore {
_subscriptions: [
cx.subscribe(&project, Self::handle_project_event),
cx.observe_release(&project, move |this, _, cx| {
- this.projects.remove(&entity_id);
+ if let Some(state) = this.projects.remove(&entity_id) {
+ if let Some(copilot) = state.copilot {
+ let shutdown = copilot
+ .update(cx, |copilot, cx| copilot.shutdown_language_server(cx));
+ cx.background_spawn(shutdown).detach();
+ }
+ }
cx.notify();
}),
],
@@ -560,6 +560,13 @@ impl ContextServerStore {
result
}
+ pub fn stop_all_servers(&mut self, cx: &mut Context<Self>) {
+ let server_ids: Vec<_> = self.servers.keys().cloned().collect();
+ for id in server_ids {
+ self.stop_server(&id, cx).log_err();
+ }
+ }
+
fn run_server(
&mut self,
server: Arc<ContextServer>,
@@ -1310,15 +1310,22 @@ impl LocalLspStore {
clangd_ext::register_notifications(lsp_store, language_server, adapter);
}
- fn shutdown_language_servers_on_quit(&mut self) -> impl Future<Output = ()> + use<> {
+ pub(crate) fn shutdown_language_servers_on_quit(&mut self) -> impl Future<Output = ()> + use<> {
let shutdown_futures = self
.language_servers
.drain()
.map(|(_, server_state)| Self::shutdown_server(server_state))
.collect::<Vec<_>>();
+ let supplementary_shutdown_futures = self
+ .supplementary_language_servers
+ .drain()
+ .filter_map(|(_, (_, server))| server.shutdown())
+ .collect::<Vec<_>>();
+
async move {
join_all(shutdown_futures).await;
+ join_all(supplementary_shutdown_futures).await;
}
}
@@ -10858,12 +10865,20 @@ impl LspStore {
.map(|state| state.id)
.collect();
local.lsp_tree.remove_nodes(&language_servers_to_stop);
+
+ let supplementary_shutdown_futures: Vec<_> = local
+ .supplementary_language_servers
+ .drain()
+ .filter_map(|(_, (_, server))| server.shutdown())
+ .collect();
+
let tasks = language_servers_to_stop
.into_iter()
.map(|server| self.stop_local_language_server(server, cx))
.collect::<Vec<_>>();
cx.background_spawn(async move {
futures::future::join_all(tasks).await;
+ futures::future::join_all(supplementary_shutdown_futures).await;
})
}
}
@@ -1928,9 +1928,29 @@ impl Project {
.detach()
}
+ let process_shutdown = |this: &mut Project, cx: &mut App| {
+ this.lsp_store.update(cx, |lsp_store, cx| {
+ if let Some(local) = lsp_store.as_local_mut() {
+ let shutdown = local.shutdown_language_servers_on_quit();
+ cx.background_spawn(shutdown).detach();
+ }
+ });
+
+ this.dap_store.update(cx, |dap_store, cx| {
+ dap_store.shutdown_sessions(cx).detach();
+ });
+
+ this.context_server_store.update(cx, |store, cx| {
+ store.stop_all_servers(cx);
+ });
+ };
+
match &self.client_state {
- ProjectClientState::Local => {}
+ ProjectClientState::Local => {
+ process_shutdown(self, cx);
+ }
ProjectClientState::Shared { .. } => {
+ process_shutdown(self, cx);
let _ = self.unshare_internal(cx);
}
ProjectClientState::Remote { remote_id, .. } => {
@@ -11320,6 +11320,64 @@ async fn test_undo_encoding_change(cx: &mut gpui::TestAppContext) {
});
}
+#[gpui::test]
+async fn test_language_servers_shut_down_on_project_release(cx: &mut gpui::TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(
+ path!("/project"),
+ json!({
+ ".git": {},
+ "main.rs": "fn main() {}"
+ }),
+ )
+ .await;
+
+ let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
+
+ let language_registry = project.read_with(cx, |project, _| project.languages().clone());
+ language_registry.add(rust_lang());
+ let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
+
+ let (buffer, _handle) = project
+ .update(cx, |project, cx| {
+ project.open_local_buffer_with_lsp(path!("/project/main.rs"), cx)
+ })
+ .await
+ .unwrap();
+
+ let mut fake_server = fake_servers.next().await.unwrap();
+ cx.executor().run_until_parked();
+
+ // Verify the server is running.
+ project.read_with(cx, |project, cx| {
+ assert!(
+ project.language_server_statuses(cx).count() > 0,
+ "language server should be running"
+ );
+ });
+
+ let held_server = project.read_with(cx, |project, cx| {
+ let lsp_store = project.lsp_store().read(cx);
+ let (server_id, _status) = lsp_store.language_server_statuses().next().unwrap();
+ lsp_store.language_server_for_id(server_id).unwrap()
+ });
+
+ drop(_handle);
+ drop(buffer);
+ drop(project);
+
+ cx.update(|_| {});
+ cx.executor().run_until_parked();
+
+ fake_server
+ .receive_notification::<lsp::notification::Exit>()
+ .await;
+
+ drop(held_server);
+}
+
pub fn init_test(cx: &mut gpui::TestAppContext) {
zlog::init_test();