feat: restart few times LSP if exited

This commit is contained in:
Kitsu 2024-04-29 22:37:24 -03:00
parent d1b8129491
commit bf3a5c99c4
3 changed files with 91 additions and 36 deletions

View File

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

View File

@ -673,10 +673,10 @@ pub fn get_by_id(&self, id: LanguageServerId) -> Option<&Arc<Client>> {
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 {
log::debug!("client was already removed");
return;
log::error!("client was already removed");
return None;
};
self.file_event_handler.remove_client(id);
let instances = self
@ -687,22 +687,23 @@ pub fn remove_by_id(&mut self, id: LanguageServerId) {
if instances.is_empty() {
self.inner_by_name.remove(client.name());
}
Some(client)
}
fn start_client(
pub fn start(
&mut self,
name: String,
ls_config: &LanguageConfiguration,
doc_path: Option<&std::path::PathBuf>,
root_dirs: &[PathBuf],
enable_snippets: bool,
) -> Result<Arc<Client>, StartupError> {
) -> Result<Option<Arc<Client>>, Error> {
let syn_loader = self.syn_loader.load();
let config = syn_loader
.language_server_configs()
.get(&name)
.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(
id,
name,
@ -716,8 +717,11 @@ fn start_client(
self.incoming.push(UnboundedReceiverStream::new(client.1));
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,
@ -748,16 +752,18 @@ pub fn restart(
});
}
}
let client = match self.start_client(
name.clone(),
language_config,
doc_path,
root_dirs,
enable_snippets,
) {
let client = match self
.start(
name.clone(),
language_config,
doc_path,
root_dirs,
enable_snippets,
)
.transpose()?
{
Ok(client) => client,
Err(StartupError::NoRequiredRootFound) => return None,
Err(StartupError::Error(err)) => return Some(Err(err)),
Err(err) => return Some(Err(err)),
};
self.inner_by_name
.insert(name.to_owned(), vec![client.clone()]);
@ -808,23 +814,22 @@ pub fn get<'a>(
return Some((name.to_owned(), Ok(client.clone())));
}
}
match self.start_client(
name.clone(),
language_config,
doc_path,
root_dirs,
enable_snippets,
) {
Ok(client) => {
self.inner_by_name
.entry(name.to_owned())
.or_default()
.push(client.clone());
Some((name.clone(), Ok(client)))
}
Err(StartupError::NoRequiredRootFound) => None,
Err(StartupError::Error(err)) => Some((name.to_owned(), Err(err))),
let client = self
.start(
name.clone(),
language_config,
doc_path,
root_dirs,
enable_snippets,
)
.transpose()?;
if let Ok(client) = &client {
self.inner_by_name
.entry(name.to_owned())
.or_default()
.push(client.clone());
}
Some((name.to_owned(), client))
},
)
}

View File

@ -25,7 +25,7 @@
compositor::{Compositor, Event},
config::Config,
handlers,
job::Jobs,
job::{Callback, Jobs},
keymap::Keymaps,
ui::{self, overlay::overlaid},
};
@ -963,7 +963,47 @@ macro_rules! language_server {
}
// 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);
}
}
}
}
}