mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 09:26:19 +04:00
feat: restart few times LSP if exited
This commit is contained in:
parent
d1b8129491
commit
bf3a5c99c4
@ -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)
|
||||
|
@ -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))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user