Add command to restart LSP server

Useful if LSP configuration changes or crashes
imgbot
Carter Green 2 years ago
parent 4b1fe367fa
commit 46ff498766

@ -62,3 +62,4 @@
| `:config-reload` | Refreshes helix's config. |
| `:config-open` | Open the helix config.toml file. |
| `:pipe` | Pipe each selection to the shell command. |
| `:lsp-restart` | Restarts the LSP server of the current buffer |

@ -12,7 +12,7 @@ use futures_util::stream::select_all::SelectAll;
use helix_core::syntax::LanguageConfiguration;
use std::{
collections::{hash_map::Entry, HashMap},
collections::HashMap,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
@ -343,52 +343,81 @@ impl Registry {
None => return Err(Error::LspNotDefined),
};
match self.inner.entry(language_config.scope.clone()) {
Entry::Occupied(entry) => Ok(entry.get().1.clone()),
Entry::Vacant(entry) => {
// initialize a new client
let id = self.counter.fetch_add(1, Ordering::Relaxed);
let (client, incoming, initialize_notify) = Client::start(
&config.command,
&config.args,
language_config.config.clone(),
&language_config.roots,
id,
)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
let client = Arc::new(client);
// Initialize the client asynchronously
let _client = client.clone();
tokio::spawn(async move {
use futures_util::TryFutureExt;
let value = _client
.capabilities
.get_or_try_init(|| {
_client
.initialize()
.map_ok(|response| response.capabilities)
})
.await;
if let Err(e) = value {
log::error!("failed to initialize language server: {}", e);
return;
}
// next up, notify<initialized>
_client
.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
.await
.unwrap();
if let Some((_, client)) = self.inner.get(&language_config.scope) {
Ok(client.clone())
} else {
let id = self.counter.fetch_add(1, Ordering::Relaxed);
let client = self.initialize_client(language_config, config, id)?; // initialize a new client
self.inner
.insert(language_config.scope.clone(), (id, client.clone()));
Ok(client)
}
}
initialize_notify.notify_one();
});
pub fn restart(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> {
let config = language_config
.language_server
.as_ref()
.ok_or(Error::LspNotDefined)?;
let id = self
.inner
.get(&language_config.scope)
.ok_or(Error::LspNotDefined)?
.0;
let new_client = self.initialize_client(language_config, config, id)?;
let (_, client) = self
.inner
.get_mut(&language_config.scope)
.ok_or(Error::LspNotDefined)?;
*client = new_client;
Ok(client.clone())
}
entry.insert((id, client.clone()));
Ok(client)
fn initialize_client(
&mut self,
language_config: &LanguageConfiguration,
config: &helix_core::syntax::LanguageServerConfiguration,
id: usize,
) -> Result<Arc<Client>> {
let (client, incoming, initialize_notify) = Client::start(
&config.command,
&config.args,
language_config.config.clone(),
&language_config.roots,
id,
)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
let client = Arc::new(client);
// Initialize the client asynchronously
let _client = client.clone();
tokio::spawn(async move {
use futures_util::TryFutureExt;
let value = _client
.capabilities
.get_or_try_init(|| {
_client
.initialize()
.map_ok(|response| response.capabilities)
})
.await;
if let Err(e) = value {
log::error!("failed to initialize language server: {}", e);
return;
}
}
// next up, notify<initialized>
_client
.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
.await
.unwrap();
initialize_notify.notify_one();
});
Ok(client)
}
pub fn iter_clients(&self) -> impl Iterator<Item = &Arc<Client>> {

@ -1077,6 +1077,16 @@ fn pipe(
Ok(())
}
fn lsp_restart(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let current_document = doc!(cx.editor).id();
cx.editor.restart_language_server(current_document);
Ok(())
}
pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "quit",
@ -1512,6 +1522,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: pipe,
completer: None,
},
TypableCommand {
name: "lsp-restart",
aliases: &[],
doc: "Restarts the LSP server of the current buffer",
fun: lsp_restart,
completer: None,
},
];
pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =

@ -475,6 +475,23 @@ impl Editor {
Self::launch_language_server(&mut self.language_servers, doc)
}
/// Restarts a language server for a given document
pub fn restart_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
let doc = self.documents.get_mut(&doc_id)?;
if let Some(language) = doc.language.as_ref() {
if let Ok(client) = self.language_servers.restart(&*language).map_err(|e| {
log::error!(
"Failed to restart the LSP for `{}` {{ {} }}",
language.scope(),
e
)
}) {
doc.set_language_server(Some(client));
}
};
Some(())
}
/// Launch a language server for a given document
fn launch_language_server(ls: &mut helix_lsp::Registry, doc: &mut Document) -> Option<()> {
// if doc doesn't have a URL it's a scratch buffer, ignore it

Loading…
Cancel
Save