diff options
Diffstat (limited to 'helix-lsp/src/lib.rs')
-rw-r--r-- | helix-lsp/src/lib.rs | 127 |
1 files changed, 121 insertions, 6 deletions
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index cff62492..49d5527f 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -13,7 +13,10 @@ use helix_core::syntax::LanguageConfiguration; use std::{ collections::{hash_map::Entry, HashMap}, - sync::Arc, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; use serde::{Deserialize, Serialize}; @@ -182,6 +185,30 @@ pub mod util { } #[derive(Debug, PartialEq, Clone)] +pub enum MethodCall { + WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams), +} + +impl MethodCall { + pub fn parse(method: &str, params: jsonrpc::Params) -> Option<MethodCall> { + use lsp::request::Request; + let request = match method { + lsp::request::WorkDoneProgressCreate::METHOD => { + let params: lsp::WorkDoneProgressCreateParams = params + .parse() + .expect("Failed to parse WorkDoneCreate params"); + Self::WorkDoneProgressCreate(params) + } + _ => { + log::warn!("unhandled lsp request: {}", method); + return None; + } + }; + Some(request) + } +} + +#[derive(Debug, PartialEq, Clone)] pub enum Notification { PublishDiagnostics(lsp::PublishDiagnosticsParams), ShowMessage(lsp::ShowMessageParams), @@ -230,9 +257,10 @@ impl Notification { #[derive(Debug)] pub struct Registry { - inner: HashMap<LanguageId, Arc<Client>>, + inner: HashMap<LanguageId, (usize, Arc<Client>)>, - pub incoming: SelectAll<UnboundedReceiverStream<Call>>, + counter: AtomicUsize, + pub incoming: SelectAll<UnboundedReceiverStream<(usize, Call)>>, } impl Default for Registry { @@ -245,10 +273,18 @@ impl Registry { pub fn new() -> Self { Self { inner: HashMap::new(), + counter: AtomicUsize::new(0), incoming: SelectAll::new(), } } + pub fn get_by_id(&mut self, id: usize) -> Option<&Client> { + self.inner + .values() + .find(|(client_id, _)| client_id == &id) + .map(|(_, client)| client.as_ref()) + } + pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> { if let Some(config) = &language_config.language_server { // avoid borrow issues @@ -256,16 +292,17 @@ impl Registry { let s_incoming = &mut self.incoming; match inner.entry(language_config.scope.clone()) { - Entry::Occupied(language_server) => Ok(language_server.get().clone()), + Entry::Occupied(entry) => Ok(entry.get().1.clone()), Entry::Vacant(entry) => { // initialize a new client - let (mut client, incoming) = Client::start(&config.command, &config.args)?; + let id = self.counter.fetch_add(1, Ordering::Relaxed); + let (mut client, incoming) = Client::start(&config.command, &config.args, id)?; // TODO: run this async without blocking futures_executor::block_on(client.initialize())?; s_incoming.push(UnboundedReceiverStream::new(incoming)); let client = Arc::new(client); - entry.insert(client.clone()); + entry.insert((id, client.clone())); Ok(client) } } @@ -273,6 +310,84 @@ impl Registry { Err(Error::LspNotDefined) } } + + pub fn iter_clients(&self) -> impl Iterator<Item = &Arc<Client>> { + self.inner.values().map(|(_, client)| client) + } +} + +#[derive(Debug)] +pub enum ProgressStatus { + Created, + Started(lsp::WorkDoneProgress), +} + +impl ProgressStatus { + pub fn progress(&self) -> Option<&lsp::WorkDoneProgress> { + match &self { + ProgressStatus::Created => None, + ProgressStatus::Started(progress) => Some(&progress), + } + } +} + +#[derive(Default, Debug)] +/// Acts as a container for progress reported by language servers. Each server +/// has a unique id assigned at creation through [`Registry`]. This id is then used +/// to store the progress in this map. +pub struct LspProgressMap(HashMap<usize, HashMap<lsp::ProgressToken, ProgressStatus>>); + +impl LspProgressMap { + pub fn new() -> Self { + Self::default() + } + + /// Returns a map of all tokens coresponding to the lanaguage server with `id`. + pub fn progress_map(&self, id: usize) -> Option<&HashMap<lsp::ProgressToken, ProgressStatus>> { + self.0.get(&id) + } + + /// Returns last progress status for a given server with `id` and `token`. + pub fn progress(&self, id: usize, token: &lsp::ProgressToken) -> Option<&ProgressStatus> { + self.0.get(&id).and_then(|values| values.get(token)) + } + + /// Checks if progress `token` for server with `id` is created. + pub fn is_created(&mut self, id: usize, token: &lsp::ProgressToken) -> bool { + self.0 + .get(&id) + .map(|values| values.get(token).is_some()) + .unwrap_or_default() + } + + pub fn create(&mut self, id: usize, token: lsp::ProgressToken) { + self.0 + .entry(id) + .or_default() + .insert(token, ProgressStatus::Created); + } + + /// Ends the progress by removing the `token` from server with `id`, if removed returns the value. + pub fn end_progress( + &mut self, + id: usize, + token: &lsp::ProgressToken, + ) -> Option<ProgressStatus> { + self.0.get_mut(&id).and_then(|vals| vals.remove(token)) + } + + /// Updates the progess of `token` for server with `id` to `status`, returns the value replaced or `None`. + pub fn update( + &mut self, + id: usize, + token: lsp::ProgressToken, + status: lsp::WorkDoneProgress, + ) -> Option<ProgressStatus> { + self.0 + .entry(id) + .or_default() + .insert(token, ProgressStatus::Started(status)) + } } // REGISTRY = HashMap<LanguageId, Lazy/OnceCell<Arc<RwLock<Client>>> |