From 2658b2801e382ed01261b4c9566759d735c8ce17 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 24 Jul 2025 18:24:53 +0300 Subject: [PATCH] Shutdown language servers better (#35038) Follow-up of https://github.com/zed-industries/zed/pull/33417 * adjust prettier mock LSP to handle `shutdown` and `exit` messages * removed another `?.log_err()` backtrace from logs and improved the logging info * always handle the last parts of the shutdown logic even if the shutdown response had failed Release Notes: - N/A --- crates/lsp/src/lsp.rs | 56 +++++++++++++------------- crates/prettier/src/prettier_server.js | 4 ++ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index a820aaf748..9978d7ebb1 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -877,39 +877,41 @@ impl LanguageServer { let server = self.server.clone(); let name = self.name.clone(); + let server_id = self.server_id; let mut timer = self.executor.timer(SERVER_SHUTDOWN_TIMEOUT).fuse(); - Some( - async move { - log::debug!("language server shutdown started"); + Some(async move { + log::debug!("language server shutdown started"); - select! { - request_result = shutdown_request.fuse() => { - match request_result { - ConnectionResult::Timeout => { - log::warn!("timeout waiting for language server {name} to shutdown"); - }, - ConnectionResult::ConnectionReset => {}, - ConnectionResult::Result(r) => r?, - } + select! { + request_result = shutdown_request.fuse() => { + match request_result { + ConnectionResult::Timeout => { + log::warn!("timeout waiting for language server {name} (id {server_id}) to shutdown"); + }, + ConnectionResult::ConnectionReset => { + log::warn!("language server {name} (id {server_id}) closed the shutdown request connection"); + }, + ConnectionResult::Result(Err(e)) => { + log::error!("Shutdown request failure, server {name} (id {server_id}): {e:#}"); + }, + ConnectionResult::Result(Ok(())) => {} } - - _ = timer => { - log::info!("timeout waiting for language server {name} to shutdown"); - }, } - response_handlers.lock().take(); - Self::notify_internal::(&outbound_tx, &()).ok(); - outbound_tx.close(); - output_done.recv().await; - server.lock().take().map(|mut child| child.kill()); - log::debug!("language server shutdown finished"); - - drop(tasks); - anyhow::Ok(()) + _ = timer => { + log::info!("timeout waiting for language server {name} (id {server_id}) to shutdown"); + }, } - .log_err(), - ) + + response_handlers.lock().take(); + Self::notify_internal::(&outbound_tx, &()).ok(); + outbound_tx.close(); + output_done.recv().await; + server.lock().take().map(|mut child| child.kill()); + drop(tasks); + log::debug!("language server shutdown finished"); + Some(()) + }) } else { None } diff --git a/crates/prettier/src/prettier_server.js b/crates/prettier/src/prettier_server.js index 6799b4aceb..b3d8a660a4 100644 --- a/crates/prettier/src/prettier_server.js +++ b/crates/prettier/src/prettier_server.js @@ -152,6 +152,10 @@ async function handleMessage(message, prettier) { throw new Error(`Message method is undefined: ${JSON.stringify(message)}`); } else if (method == "initialized") { return; + } else if (method === "shutdown") { + sendResponse({ result: {} }); + } else if (method == "exit") { + process.exit(0); } if (id === undefined) {