Handle language server termination (#4797)
This change handles a language server exiting. This was a UX sore-spot: if a language server crashed, Helix did not recognize the exit and continued to send requests to it. All requests would timeout since they would not receive responses. This would also hold-up Helix closing itself down since it would try to gracefully shutdown the server which is implemented in the LSP spec as a request. We could attempt to automatically restart the language server on crash. I left this for future work since that change will need to be slightly complicated: it will need to cover the case of a language server repeatedly crashing.
This commit is contained in:
parent
598bd8bf11
commit
8be2d1dcbf
3 changed files with 53 additions and 0 deletions
|
@ -282,6 +282,8 @@ impl MethodCall {
|
||||||
pub enum Notification {
|
pub enum Notification {
|
||||||
// we inject this notification to signal the LSP is ready
|
// we inject this notification to signal the LSP is ready
|
||||||
Initialized,
|
Initialized,
|
||||||
|
// and this notification to signal that the LSP exited
|
||||||
|
Exit,
|
||||||
PublishDiagnostics(lsp::PublishDiagnosticsParams),
|
PublishDiagnostics(lsp::PublishDiagnosticsParams),
|
||||||
ShowMessage(lsp::ShowMessageParams),
|
ShowMessage(lsp::ShowMessageParams),
|
||||||
LogMessage(lsp::LogMessageParams),
|
LogMessage(lsp::LogMessageParams),
|
||||||
|
@ -294,6 +296,7 @@ impl Notification {
|
||||||
|
|
||||||
let notification = match method {
|
let notification = match method {
|
||||||
lsp::notification::Initialized::METHOD => Self::Initialized,
|
lsp::notification::Initialized::METHOD => Self::Initialized,
|
||||||
|
lsp::notification::Exit::METHOD => Self::Exit,
|
||||||
lsp::notification::PublishDiagnostics::METHOD => {
|
lsp::notification::PublishDiagnostics::METHOD => {
|
||||||
let params: lsp::PublishDiagnosticsParams = params.parse()?;
|
let params: lsp::PublishDiagnosticsParams = params.parse()?;
|
||||||
Self::PublishDiagnostics(params)
|
Self::PublishDiagnostics(params)
|
||||||
|
@ -350,6 +353,10 @@ impl Registry {
|
||||||
.map(|(_, client)| client.as_ref())
|
.map(|(_, client)| client.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_by_id(&mut self, id: usize) {
|
||||||
|
self.inner.retain(|_, (client_id, _)| client_id != &id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn restart(
|
pub fn restart(
|
||||||
&mut self,
|
&mut self,
|
||||||
language_config: &LanguageConfiguration,
|
language_config: &LanguageConfiguration,
|
||||||
|
|
|
@ -250,6 +250,26 @@ impl Transport {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Err(Error::StreamClosed) => {
|
||||||
|
// 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)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
error!("err: <- {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("err: <- {:?}", err);
|
error!("err: <- {:?}", err);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -871,6 +871,32 @@ impl Application {
|
||||||
Notification::ProgressMessage(_params) => {
|
Notification::ProgressMessage(_params) => {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
Notification::Exit => {
|
||||||
|
self.editor.set_status("Language server exited");
|
||||||
|
|
||||||
|
// Clear any diagnostics for documents with this server open.
|
||||||
|
let urls: Vec<_> = self
|
||||||
|
.editor
|
||||||
|
.documents_mut()
|
||||||
|
.filter_map(|doc| {
|
||||||
|
if doc.language_server().map(|server| server.id())
|
||||||
|
== Some(server_id)
|
||||||
|
{
|
||||||
|
doc.set_diagnostics(Vec::new());
|
||||||
|
doc.url()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for url in urls {
|
||||||
|
self.editor.diagnostics.remove(&url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the language server from the registry.
|
||||||
|
self.editor.language_servers.remove_by_id(server_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
|
Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
|
||||||
|
|
Loading…
Add table
Reference in a new issue