From 385ccdfc9c4f04cbe33c7cfdd668a1f694b6d870 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Tue, 20 Sep 2022 08:44:36 +0100 Subject: add :lsp-restart command (#3435) --- helix-lsp/src/lib.rs | 118 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 37 deletions(-) (limited to 'helix-lsp') diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 8d43410a..cb234357 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -9,7 +9,8 @@ pub use lsp::{Position, Url}; pub use lsp_types as lsp; use futures_util::stream::select_all::SelectAll; -use helix_core::syntax::LanguageConfiguration; +use helix_core::syntax::{LanguageConfiguration, LanguageServerConfiguration}; +use tokio::sync::mpsc::UnboundedReceiver; use std::{ collections::{hash_map::Entry, HashMap}, @@ -335,6 +336,33 @@ impl Registry { .map(|(_, client)| client.as_ref()) } + pub fn restart( + &mut self, + language_config: &LanguageConfiguration, + ) -> Result>> { + let config = match &language_config.language_server { + Some(config) => config, + None => return Ok(None), + }; + + let scope = language_config.scope.clone(); + + match self.inner.entry(scope) { + Entry::Vacant(_) => Ok(None), + Entry::Occupied(mut entry) => { + // initialize a new client + let id = self.counter.fetch_add(1, Ordering::Relaxed); + + let NewClientResult(client, incoming) = start_client(id, language_config, config)?; + self.incoming.push(UnboundedReceiverStream::new(incoming)); + + entry.insert((id, client.clone())); + + Ok(Some(client)) + } + } + } + pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result>> { let config = match &language_config.language_server { Some(config) => config, @@ -346,43 +374,9 @@ impl Registry { 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, - config.timeout, - )?; + + let NewClientResult(client, incoming) = start_client(id, language_config, config)?; 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 - _client - .notify::(lsp::InitializedParams {}) - .await - .unwrap(); - - initialize_notify.notify_one(); - }); entry.insert((id, client.clone())); Ok(Some(client)) @@ -473,6 +467,56 @@ impl LspProgressMap { } } +struct NewClientResult(Arc, UnboundedReceiver<(usize, Call)>); + +/// start_client takes both a LanguageConfiguration and a LanguageServerConfiguration to ensure that +/// it is only called when it makes sense. +fn start_client( + id: usize, + config: &LanguageConfiguration, + ls_config: &LanguageServerConfiguration, +) -> Result { + let (client, incoming, initialize_notify) = Client::start( + &ls_config.command, + &ls_config.args, + config.config.clone(), + &config.roots, + id, + ls_config.timeout, + )?; + + 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 + _client + .notify::(lsp::InitializedParams {}) + .await + .unwrap(); + + initialize_notify.notify_one(); + }); + + Ok(NewClientResult(client, incoming)) +} + #[cfg(test)] mod tests { use super::{lsp, util::*, OffsetEncoding}; -- cgit v1.2.3-70-g09d2