on master help sign in
add :lsp-restart command (#3435)
commit385ccdfc9c4f04cbe33c7cfdd668a1f694b6d870 authorFilipe Azevedo <filipe@azevedo.io> dateTue 20 Sep 2022, 08:44 (4 years ago) parentaa00a47
3 files changed+123−37
book/src/generated/typable-cmd.md+1 helix-lsp/src/lib.rs+81−37 helix-term/src/commands/typed.rs+41
book/src/generated/typable-cmd.md+1
@@ -44,6 +44,7 @@
4444| `:show-directory`, `:pwd` | Show the current working directory. |
4545| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. |
4646| `:reload` | Discard changes and reload from the source file. |
47+| `:lsp-restart` | Restarts the Language Server that is in use by the current doc |
4748| `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. |
4849| `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. |
4950| `:debug-remote`, `:dbg-tcp` | Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters. |
helix-lsp/src/lib.rs+81−37
@@ -9,7 +9,8 @@ pub use lsp::{Position, Url};
99pub use lsp_types as lsp;
1010
1111use futures_util::stream::select_all::SelectAll;
12−use helix_core::syntax::LanguageConfiguration;
12+use helix_core::syntax::{LanguageConfiguration, LanguageServerConfiguration};
13+use tokio::sync::mpsc::UnboundedReceiver;
1314
1415use std::{
1516 collections::{hash_map::Entry, HashMap},
@@ -335,6 +336,33 @@ impl Registry {
335336 .map(|(_, client)| client.as_ref())
336337 }
337338
339+ pub fn restart(
340+ &mut self,
341+ language_config: &LanguageConfiguration,
342+ ) -> Result<Option<Arc<Client>>> {
343+ let config = match &language_config.language_server {
344+ Some(config) => config,
345+ None => return Ok(None),
346+ };
347+
348+ let scope = language_config.scope.clone();
349+
350+ match self.inner.entry(scope) {
351+ Entry::Vacant(_) => Ok(None),
352+ Entry::Occupied(mut entry) => {
353+ // initialize a new client
354+ let id = self.counter.fetch_add(1, Ordering::Relaxed);
355+
356+ let NewClientResult(client, incoming) = start_client(id, language_config, config)?;
357+ self.incoming.push(UnboundedReceiverStream::new(incoming));
358+
359+ entry.insert((id, client.clone()));
360+
361+ Ok(Some(client))
362+ }
363+ }
364+ }
365+
338366 pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Option<Arc<Client>>> {
339367 let config = match &language_config.language_server {
340368 Some(config) => config,
@@ -346,43 +374,9 @@ impl Registry {
346374 Entry::Vacant(entry) => {
347375 // initialize a new client
348376 let id = self.counter.fetch_add(1, Ordering::Relaxed);
349− let (client, incoming, initialize_notify) = Client::start(
350− &config.command,
351− &config.args,
352− language_config.config.clone(),
353− &language_config.roots,
354− id,
355− config.timeout,
356− )?;
377+
378+ let NewClientResult(client, incoming) = start_client(id, language_config, config)?;
357379 self.incoming.push(UnboundedReceiverStream::new(incoming));
358− let client = Arc::new(client);
359−
360− // Initialize the client asynchronously
361− let _client = client.clone();
362− tokio::spawn(async move {
363− use futures_util::TryFutureExt;
364− let value = _client
365− .capabilities
366− .get_or_try_init(|| {
367− _client
368− .initialize()
369− .map_ok(|response| response.capabilities)
370− })
371− .await;
372−
373− if let Err(e) = value {
374− log::error!("failed to initialize language server: {}", e);
375− return;
376− }
377−
378− // next up, notify<initialized>
379− _client
380− .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
381− .await
382− .unwrap();
383−
384− initialize_notify.notify_one();
385− });
386380
387381 entry.insert((id, client.clone()));
388382 Ok(Some(client))
@@ -473,6 +467,56 @@ impl LspProgressMap {
473467 }
474468}
475469
470+struct NewClientResult(Arc<Client>, UnboundedReceiver<(usize, Call)>);
471+
472+/// start_client takes both a LanguageConfiguration and a LanguageServerConfiguration to ensure that
473+/// it is only called when it makes sense.
474+fn start_client(
475+ id: usize,
476+ config: &LanguageConfiguration,
477+ ls_config: &LanguageServerConfiguration,
478+) -> Result<NewClientResult> {
479+ let (client, incoming, initialize_notify) = Client::start(
480+ &ls_config.command,
481+ &ls_config.args,
482+ config.config.clone(),
483+ &config.roots,
484+ id,
485+ ls_config.timeout,
486+ )?;
487+
488+ let client = Arc::new(client);
489+
490+ // Initialize the client asynchronously
491+ let _client = client.clone();
492+ tokio::spawn(async move {
493+ use futures_util::TryFutureExt;
494+ let value = _client
495+ .capabilities
496+ .get_or_try_init(|| {
497+ _client
498+ .initialize()
499+ .map_ok(|response| response.capabilities)
500+ })
501+ .await;
502+
503+ if let Err(e) = value {
504+ log::error!("failed to initialize language server: {}", e);
505+ return;
506+ }
507+
508+ // next up, notify<initialized>
509+ _client
510+ .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
511+ .await
512+ .unwrap();
513+
514+ initialize_notify.notify_one();
515+ });
516+
517+ Ok(NewClientResult(client, incoming))
518+}
519+
476520#[cfg(test)]
477521mod tests {
478522 use super::{lsp, util::*, OffsetEncoding};
helix-term/src/commands/typed.rs+41
@@ -985,6 +985,40 @@ fn reload(
985985 })
986986}
987987
988+fn lsp_restart(
989+ cx: &mut compositor::Context,
990+ _args: &[Cow<str>],
991+ event: PromptEvent,
992+) -> anyhow::Result<()> {
993+ if event != PromptEvent::Validate {
994+ return Ok(());
995+ }
996+
997+ let (_view, doc) = current!(cx.editor);
998+ let config = doc
999+ .language_config()
1000+ .context("LSP not defined for the current document")?;
1001+
1002+ let scope = config.scope.clone();
1003+ cx.editor.language_servers.restart(config)?;
1004+
1005+ // This collect is needed because refresh_language_server would need to re-borrow editor.
1006+ let document_ids_to_refresh: Vec<DocumentId> = cx
1007+ .editor
1008+ .documents()
1009+ .filter_map(|doc| match doc.language_config() {
1010+ Some(config) if config.scope.eq(&scope) => Some(doc.id()),
1011+ _ => None,
1012+ })
1013+ .collect();
1014+
1015+ for document_id in document_ids_to_refresh {
1016+ cx.editor.refresh_language_server(document_id);
1017+ }
1018+
1019+ Ok(())
1020+}
1021+
9881022fn tree_sitter_scopes(
9891023 cx: &mut compositor::Context,
9901024 _args: &[Cow<str>],
@@ -1837,6 +1871,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
18371871 fun: reload,
18381872 completer: None,
18391873 },
1874+ TypableCommand {
1875+ name: "lsp-restart",
1876+ aliases: &[],
1877+ doc: "Restarts the Language Server that is in use by the current doc",
1878+ fun: lsp_restart,
1879+ completer: None,
1880+ },
18401881 TypableCommand {
18411882 name: "tree-sitter-scopes",
18421883 aliases: &[],