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:
5ceb2238d3/runtime/lua/vim/lsp.lua (L1610-L1628)
This commit is contained in:
Michael Davis 2023-06-29 10:25:23 -05:00 committed by GitHub
parent b745fb2551
commit e0bb032f0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -353,6 +353,11 @@ fn is_initialize(payload: &Payload) -> bool {
}
}
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 @@ fn is_initialize(payload: &Payload) -> bool {
}
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;