feat: restart few times LSP if exited

pull/10640/head
Kitsu 7 months ago
parent d1b8129491
commit bf3a5c99c4

@ -17,7 +17,7 @@ use parking_lot::Mutex;
use serde::Deserialize; use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
use std::sync::{ use std::sync::{
atomic::{AtomicU64, Ordering}, atomic::{AtomicU64, AtomicU8, Ordering},
Arc, Arc,
}; };
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
@ -59,6 +59,7 @@ pub struct Client {
initialize_notify: Arc<Notify>, initialize_notify: Arc<Notify>,
/// workspace folders added while the server is still initializing /// workspace folders added while the server is still initializing
req_timeout: u64, req_timeout: u64,
restarts_left: AtomicU8,
} }
impl Client { impl Client {
@ -231,6 +232,7 @@ impl Client {
root_uri, root_uri,
workspace_folders: Mutex::new(workspace_folders), workspace_folders: Mutex::new(workspace_folders),
initialize_notify: initialize_notify.clone(), initialize_notify: initialize_notify.clone(),
restarts_left: AtomicU8::new(2),
}; };
Ok((client, server_rx, initialize_notify)) Ok((client, server_rx, initialize_notify))
@ -244,6 +246,14 @@ impl Client {
self.id self.id
} }
pub fn set_restarts_left(&self, x: u8) {
self.restarts_left.store(x, Ordering::Relaxed);
}
pub fn restarts_left(&self) -> u8 {
self.restarts_left.load(Ordering::Relaxed)
}
fn next_request_id(&self) -> jsonrpc::Id { fn next_request_id(&self) -> jsonrpc::Id {
let id = self.request_counter.fetch_add(1, Ordering::Relaxed); let id = self.request_counter.fetch_add(1, Ordering::Relaxed);
jsonrpc::Id::Num(id) jsonrpc::Id::Num(id)

@ -673,10 +673,10 @@ impl Registry {
self.inner.get(id) self.inner.get(id)
} }
pub fn remove_by_id(&mut self, id: LanguageServerId) { pub fn remove_by_id(&mut self, id: LanguageServerId) -> Option<Arc<Client>> {
let Some(client) = self.inner.remove(id) else { let Some(client) = self.inner.remove(id) else {
log::debug!("client was already removed"); log::error!("client was already removed");
return; return None;
}; };
self.file_event_handler.remove_client(id); self.file_event_handler.remove_client(id);
let instances = self let instances = self
@ -687,22 +687,23 @@ impl Registry {
if instances.is_empty() { if instances.is_empty() {
self.inner_by_name.remove(client.name()); self.inner_by_name.remove(client.name());
} }
Some(client)
} }
fn start_client( pub fn start(
&mut self, &mut self,
name: String, name: String,
ls_config: &LanguageConfiguration, ls_config: &LanguageConfiguration,
doc_path: Option<&std::path::PathBuf>, doc_path: Option<&std::path::PathBuf>,
root_dirs: &[PathBuf], root_dirs: &[PathBuf],
enable_snippets: bool, enable_snippets: bool,
) -> Result<Arc<Client>, StartupError> { ) -> Result<Option<Arc<Client>>, Error> {
let syn_loader = self.syn_loader.load(); let syn_loader = self.syn_loader.load();
let config = syn_loader let config = syn_loader
.language_server_configs() .language_server_configs()
.get(&name) .get(&name)
.ok_or_else(|| anyhow::anyhow!("Language server '{name}' not defined"))?; .ok_or_else(|| anyhow::anyhow!("Language server '{name}' not defined"))?;
let id = self.inner.try_insert_with_key(|id| { match self.inner.try_insert_with_key(|id| {
start_client( start_client(
id, id,
name, name,
@ -716,8 +717,11 @@ impl Registry {
self.incoming.push(UnboundedReceiverStream::new(client.1)); self.incoming.push(UnboundedReceiverStream::new(client.1));
client.0 client.0
}) })
})?; }) {
Ok(self.inner[id].clone()) Ok(id) => Ok(Some(self.inner[id].clone())),
Err(StartupError::NoRequiredRootFound) => Ok(None),
Err(StartupError::Error(err)) => Err(err),
}
} }
/// If this method is called, all documents that have a reference to language servers used by the language config have to refresh their language servers, /// If this method is called, all documents that have a reference to language servers used by the language config have to refresh their language servers,
@ -748,16 +752,18 @@ impl Registry {
}); });
} }
} }
let client = match self.start_client( let client = match self
name.clone(), .start(
language_config, name.clone(),
doc_path, language_config,
root_dirs, doc_path,
enable_snippets, root_dirs,
) { enable_snippets,
)
.transpose()?
{
Ok(client) => client, Ok(client) => client,
Err(StartupError::NoRequiredRootFound) => return None, Err(err) => return Some(Err(err)),
Err(StartupError::Error(err)) => return Some(Err(err)),
}; };
self.inner_by_name self.inner_by_name
.insert(name.to_owned(), vec![client.clone()]); .insert(name.to_owned(), vec![client.clone()]);
@ -808,23 +814,22 @@ impl Registry {
return Some((name.to_owned(), Ok(client.clone()))); return Some((name.to_owned(), Ok(client.clone())));
} }
} }
match self.start_client( let client = self
name.clone(), .start(
language_config, name.clone(),
doc_path, language_config,
root_dirs, doc_path,
enable_snippets, root_dirs,
) { enable_snippets,
Ok(client) => { )
self.inner_by_name .transpose()?;
.entry(name.to_owned()) if let Ok(client) = &client {
.or_default() self.inner_by_name
.push(client.clone()); .entry(name.to_owned())
Some((name.clone(), Ok(client))) .or_default()
} .push(client.clone());
Err(StartupError::NoRequiredRootFound) => None,
Err(StartupError::Error(err)) => Some((name.to_owned(), Err(err))),
} }
Some((name.to_owned(), client))
}, },
) )
} }

@ -25,7 +25,7 @@ use crate::{
compositor::{Compositor, Event}, compositor::{Compositor, Event},
config::Config, config::Config,
handlers, handlers,
job::Jobs, job::{Callback, Jobs},
keymap::Keymaps, keymap::Keymaps,
ui::{self, overlay::overlaid}, ui::{self, overlay::overlaid},
}; };
@ -963,7 +963,47 @@ impl Application {
} }
// Remove the language server from the registry. // Remove the language server from the registry.
self.editor.language_servers.remove_by_id(server_id); let client = self.editor.language_servers.remove_by_id(server_id);
if let Some(client) = client {
let name = client.name().to_owned();
let restarts = client.restarts_left();
if let Some(restarts) = restarts.checked_sub(1) {
let job = async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
Ok(Callback::Editor(Box::new(move |editor: &mut Editor| {
let editor_config = &editor.config();
let (_, doc) = {
let view = view_mut!(editor);
let id = view.doc;
let doc = doc_mut!(editor, &id);
(view, doc)
};
let doc_path = doc.path();
let Some(lang_config) = doc.language_config() else {
log::warn!("at LSP restart config is missing");
return;
};
match editor.language_servers.start(
name,
lang_config,
doc_path,
&editor_config.workspace_lsp_roots,
editor_config.lsp.snippets,
) {
Ok(Some(client)) => client.set_restarts_left(restarts),
Ok(None) => {}
Err(err) => {
log::warn!("failed to restart LSP: {:?}", err);
}
}
})))
};
self.jobs.callback(job);
}
}
} }
} }
} }

Loading…
Cancel
Save