From 008074d436414bc0614ea10a474bce08f4a6a2c5 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 29 Jun 2023 10:25:23 -0500 Subject: [PATCH] LSP: Forcefully shutdown uninitialized servers (#7449) The LSP spec has this to say about initialize: > Until the server has responded to the `initialize` request with an > `InitializeResult`, the client must not send any additional requests > or notifications to the server. (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize) The spec is not really explicit about how to handle this scenario. Before a client sends the 'initialize' request we are allowed to send an 'exit' notification, but after 'initialize' we can't send any requests (like shutdown) or notifications (like exit). So my intepretation is that we should forcefully close the server in this state. This matches the behavior of Neovim's built-in LSP client: https://github.com/neovim/neovim/blob/5ceb2238d3685255cd517ca87fd7edae9ed88811/runtime/lua/vim/lsp.lua#L1610-L1628 --- helix-lsp/src/transport.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/helix-lsp/src/transport.rs b/helix-lsp/src/transport.rs index 8c38c1773c19d..9fdd30aa01cf8 100644 --- a/helix-lsp/src/transport.rs +++ b/helix-lsp/src/transport.rs @@ -353,6 +353,11 @@ impl Transport { } } + fn is_shutdown(payload: &Payload) -> bool { + use lsp_types::request::{Request, Shutdown}; + matches!(payload, Payload::Request { value: jsonrpc::MethodCall { method, .. }, .. } if method == Shutdown::METHOD) + } + // TODO: events that use capabilities need to do the right thing loop { @@ -391,7 +396,10 @@ impl Transport { } msg = client_rx.recv() => { if let Some(msg) = msg { - if is_pending && !is_initialize(&msg) { + if is_pending && is_shutdown(&msg) { + log::info!("Language server not initialized, shutting down"); + break; + } else if is_pending && !is_initialize(&msg) { // ignore notifications if let Payload::Notification(_) = msg { continue;