lsp: Correctly serialize errors for LSP requests + improve handling of unrecognized methods (#35738)

We used to not respond at all to requests that we didn't have a handler
for, which is yuck. It may have left the language server waiting for the
response for no good reason. The other (worse) finding is that we did
not have a full definition of an Error type for LSP, which made it so
that a spec-compliant language server would fail to deserialize our
response (with an error). This then could lead to all sorts of
funkiness, including hangs and crashes on the language server's part.

Co-authored-by: Lukas <lukas@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>

Co-authored-by: Anthony Eid <hello@anthonyeid.me>

Closes #ISSUE

Release Notes:

- Improved reporting of errors to language servers, which should improve
the stability of LSPs ran by Zed.

---------

Co-authored-by: Lukas <lukas@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
Piotr Osiewicz 2025-08-06 20:27:48 +02:00 committed by GitHub
parent 794098e5c9
commit fb1f9d1212
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 24 deletions

View file

@ -13,14 +13,15 @@ use parking_lot::Mutex;
use smol::io::BufReader;
use crate::{
AnyNotification, AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, RequestId, ResponseHandler,
AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, NotificationOrRequest, RequestId,
ResponseHandler,
};
const HEADER_DELIMITER: &[u8; 4] = b"\r\n\r\n";
/// Handler for stdout of language server.
pub struct LspStdoutHandler {
pub(super) loop_handle: Task<Result<()>>,
pub(super) notifications_channel: UnboundedReceiver<AnyNotification>,
pub(super) incoming_messages: UnboundedReceiver<NotificationOrRequest>,
}
async fn read_headers<Stdout>(reader: &mut BufReader<Stdout>, buffer: &mut Vec<u8>) -> Result<()>
@ -54,13 +55,13 @@ impl LspStdoutHandler {
let loop_handle = cx.spawn(Self::handler(stdout, tx, response_handlers, io_handlers));
Self {
loop_handle,
notifications_channel,
incoming_messages: notifications_channel,
}
}
async fn handler<Input>(
stdout: Input,
notifications_sender: UnboundedSender<AnyNotification>,
notifications_sender: UnboundedSender<NotificationOrRequest>,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
) -> anyhow::Result<()>
@ -96,7 +97,7 @@ impl LspStdoutHandler {
}
}
if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
if let Ok(msg) = serde_json::from_slice::<NotificationOrRequest>(&buffer) {
notifications_sender.unbounded_send(msg)?;
} else if let Ok(AnyResponse {
id, error, result, ..