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 {
|
||||
// we inject this notification to signal the LSP is ready
|
||||
Initialized,
|
||||
// and this notification to signal that the LSP exited
|
||||
Exit,
|
||||
PublishDiagnostics(lsp::PublishDiagnosticsParams),
|
||||
ShowMessage(lsp::ShowMessageParams),
|
||||
LogMessage(lsp::LogMessageParams),
|
||||
|
@ -294,6 +296,7 @@ impl Notification {
|
|||
|
||||
let notification = match method {
|
||||
lsp::notification::Initialized::METHOD => Self::Initialized,
|
||||
lsp::notification::Exit::METHOD => Self::Exit,
|
||||
lsp::notification::PublishDiagnostics::METHOD => {
|
||||
let params: lsp::PublishDiagnosticsParams = params.parse()?;
|
||||
Self::PublishDiagnostics(params)
|
||||
|
@ -350,6 +353,10 @@ impl Registry {
|
|||
.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(
|
||||
&mut self,
|
||||
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) => {
|
||||
error!("err: <- {:?}", err);
|
||||
break;
|
||||
|
|
|
@ -871,6 +871,32 @@ impl Application {
|
|||
Notification::ProgressMessage(_params) => {
|
||||
// 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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue