Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exit a language server if it sends a message with invalid json #9332

Merged
67 changes: 39 additions & 28 deletions helix-lsp/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,39 @@ impl Transport {
mut server_stdout: BufReader<ChildStdout>,
client_tx: UnboundedSender<(usize, jsonrpc::Call)>,
) {
async fn exit_language_server(
bendennis marked this conversation as resolved.
Show resolved Hide resolved
transport: &Arc<Transport>,
client_tx: &UnboundedSender<(usize, crate::Call)>,
) {
// Close any outstanding requests.
for (id, tx) in transport.pending_requests.lock().await.drain() {
match tx.send(Err(Error::StreamClosed)).await {
Ok(_) => (),
Err(_) => {
error!("Could not close request on a closed channel (id={:?})", id)
}
}
}

// Hack: inject a terminated notification so we trigger code that needs to happen after exit
use lsp_types::notification::Notification as _;
let notification =
ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
jsonrpc: None,
method: lsp_types::notification::Exit::METHOD.to_string(),
params: jsonrpc::Params::None,
}));
match transport
.process_server_message(client_tx, notification, &transport.name)
.await
{
Ok(_) => {}
Err(err) => {
error!("err: <- {:?}", err);
}
}
}

let mut recv_buffer = String::new();
loop {
match Self::recv_server_message(&mut server_stdout, &mut recv_buffer, &transport.name)
Expand All @@ -271,37 +304,15 @@ impl Transport {
};
}
Err(Error::StreamClosed) => {
// Close any outstanding requests.
for (id, tx) in transport.pending_requests.lock().await.drain() {
bendennis marked this conversation as resolved.
Show resolved Hide resolved
match tx.send(Err(Error::StreamClosed)).await {
Ok(_) => (),
Err(_) => {
error!("Could not close request on a closed channel (id={:?})", id)
}
}
}

// Hack: inject a terminated notification so we trigger code that needs to happen after exit
use lsp_types::notification::Notification as _;
let notification =
ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
jsonrpc: None,
method: lsp_types::notification::Exit::METHOD.to_string(),
params: jsonrpc::Params::None,
}));
match transport
.process_server_message(&client_tx, notification, &transport.name)
.await
{
Ok(_) => {}
Err(err) => {
error!("err: <- {:?}", err);
}
}
exit_language_server(&transport, &client_tx).await;
break;
}
Err(err) => {
error!("{} err: <- {err:?}", transport.name);
error!(
"Exiting {} after unexpected error: {err:?}",
&transport.name
);
exit_language_server(&transport, &client_tx).await;
break;
}
}
Expand Down
4 changes: 2 additions & 2 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,8 @@ impl Application {
let notification = match Notification::parse(&method, params) {
Ok(notification) => notification,
Err(err) => {
bendennis marked this conversation as resolved.
Show resolved Hide resolved
log::error!(
"received malformed notification from Language Server: {}",
log::info!(
"Ignoring unknown notification from Language Server: {}",
err
);
return;
Expand Down
Loading