Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/handlers/signature_help.rs')
| -rw-r--r-- | helix-term/src/handlers/signature_help.rs | 168 |
1 files changed, 67 insertions, 101 deletions
diff --git a/helix-term/src/handlers/signature_help.rs b/helix-term/src/handlers/signature_help.rs index 8a0c9754..3c746548 100644 --- a/helix-term/src/handlers/signature_help.rs +++ b/helix-term/src/handlers/signature_help.rs @@ -1,9 +1,11 @@ use std::sync::Arc; use std::time::Duration; -use helix_core::syntax::config::LanguageServerFeature; -use helix_event::{cancelable_future, register_hook, send_blocking, TaskController, TaskHandle}; -use helix_lsp::lsp::{self, SignatureInformation}; +use helix_core::syntax::LanguageServerFeature; +use helix_event::{ + cancelable_future, cancelation, register_hook, send_blocking, CancelRx, CancelTx, +}; +use helix_lsp::lsp; use helix_stdx::rope::RopeSliceExt; use helix_view::document::Mode; use helix_view::events::{DocumentDidChange, SelectionDidChange}; @@ -16,15 +18,15 @@ use crate::commands::Open; use crate::compositor::Compositor; use crate::events::{OnModeSwitch, PostInsertChar}; use crate::handlers::Handlers; -use crate::ui::lsp::signature_help::{Signature, SignatureHelp}; +use crate::ui::lsp::SignatureHelp; use crate::ui::Popup; use crate::{job, ui}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] enum State { Open, Closed, - Pending, + Pending { request: CancelTx }, } /// debounce timeout in ms, value taken from VSCode @@ -35,7 +37,6 @@ const TIMEOUT: u64 = 120; pub(super) struct SignatureHelpHandler { trigger: Option<SignatureHelpInvoked>, state: State, - task_controller: TaskController, } impl SignatureHelpHandler { @@ -43,7 +44,6 @@ impl SignatureHelpHandler { SignatureHelpHandler { trigger: None, state: State::Closed, - task_controller: TaskController::new(), } } } @@ -76,12 +76,12 @@ impl helix_event::AsyncHook for SignatureHelpHandler { } SignatureHelpEvent::RequestComplete { open } => { // don't cancel rerequest that was already triggered - if self.state == State::Pending && self.task_controller.is_running() { - return timeout; + if let State::Pending { request } = &self.state { + if !request.is_closed() { + return timeout; + } } self.state = if open { State::Open } else { State::Closed }; - self.task_controller.cancel(); - return timeout; } } @@ -93,16 +93,16 @@ impl helix_event::AsyncHook for SignatureHelpHandler { fn finish_debounce(&mut self) { let invocation = self.trigger.take().unwrap(); - self.state = State::Pending; - let handle = self.task_controller.restart(); - job::dispatch_blocking(move |editor, _| request_signature_help(editor, invocation, handle)) + let (tx, rx) = cancelation(); + self.state = State::Pending { request: tx }; + job::dispatch_blocking(move |editor, _| request_signature_help(editor, invocation, rx)) } } pub fn request_signature_help( editor: &mut Editor, invoked: SignatureHelpInvoked, - cancel: TaskHandle, + cancel: CancelRx, ) { let (view, doc) = current!(editor); @@ -118,7 +118,8 @@ pub fn request_signature_help( // Do not show the message if signature help was invoked // automatically on backspace, trigger characters, etc. if invoked == SignatureHelpInvoked::Manual { - editor.set_error("No configured language server supports signature-help"); + editor + .set_error("No configured language server supports signature-help"); } return; }; @@ -137,31 +138,6 @@ pub fn request_signature_help( }); } -fn active_param_range( - signature: &SignatureInformation, - response_active_parameter: Option<u32>, -) -> Option<(usize, usize)> { - let param_idx = signature - .active_parameter - .or(response_active_parameter) - .unwrap_or(0) as usize; - let param = signature.parameters.as_ref()?.get(param_idx)?; - match ¶m.label { - lsp::ParameterLabel::Simple(string) => { - let start = signature.label.find(string.as_str())?; - Some((start, start + string.len())) - } - lsp::ParameterLabel::LabelOffsets([start, end]) => { - // LS sends offsets based on utf-16 based string representation - // but highlighting in helix is done using byte offset. - use helix_core::str_utils::char_to_byte_idx; - let from = char_to_byte_idx(&signature.label, *start as usize); - let to = char_to_byte_idx(&signature.label, *end as usize); - Some((from, to)) - } - } -} - pub fn show_signature_help( editor: &mut Editor, compositor: &mut Compositor, @@ -208,64 +184,54 @@ pub fn show_signature_help( let doc = doc!(editor); let language = doc.language_name().unwrap_or(""); - if response.signatures.is_empty() { - return; - } - - let signatures: Vec<Signature> = response + let signature = match response .signatures - .into_iter() - .map(|s| { - let active_param_range = active_param_range(&s, response.active_parameter); - - let signature_doc = if config.lsp.display_signature_help_docs { - s.documentation.map(|doc| match doc { - lsp::Documentation::String(s) => s, - lsp::Documentation::MarkupContent(markup) => markup.value, - }) - } else { - None - }; - - Signature { - signature: s.label, - signature_doc, - active_param_range, - } - }) - .collect(); - - let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID); - let lsp_signature = response.active_signature.map(|s| s as usize); - - // take the new suggested lsp signature if changed - // otherwise take the old signature if possible - // otherwise the last one (in case there is less signatures than before) - let active_signature = old_popup - .as_ref() - .map(|popup| { - let old_lsp_sig = popup.contents().lsp_signature(); - let old_sig = popup - .contents() - .active_signature() - .min(signatures.len() - 1); - - if old_lsp_sig != lsp_signature { - lsp_signature.unwrap_or(old_sig) - } else { - old_sig - } - }) - .unwrap_or(lsp_signature.unwrap_or_default()); - - let contents = SignatureHelp::new( + .get(response.active_signature.unwrap_or(0) as usize) + { + Some(s) => s, + None => return, + }; + let mut contents = SignatureHelp::new( + signature.label.clone(), language.to_string(), Arc::clone(&editor.syn_loader), - active_signature, - lsp_signature, - signatures, ); + let signature_doc = if config.lsp.display_signature_help_docs { + signature.documentation.as_ref().map(|doc| match doc { + lsp::Documentation::String(s) => s.clone(), + lsp::Documentation::MarkupContent(markup) => markup.value.clone(), + }) + } else { + None + }; + + contents.set_signature_doc(signature_doc); + + let active_param_range = || -> Option<(usize, usize)> { + let param_idx = signature + .active_parameter + .or(response.active_parameter) + .unwrap_or(0) as usize; + let param = signature.parameters.as_ref()?.get(param_idx)?; + match ¶m.label { + lsp::ParameterLabel::Simple(string) => { + let start = signature.label.find(string.as_str())?; + Some((start, start + string.len())) + } + lsp::ParameterLabel::LabelOffsets([start, end]) => { + // LS sends offsets based on utf-16 based string representation + // but highlighting in helix is done using byte offset. + use helix_core::str_utils::char_to_byte_idx; + let from = char_to_byte_idx(&signature.label, *start as usize); + let to = char_to_byte_idx(&signature.label, *end as usize); + Some((from, to)) + } + } + }; + contents.set_active_param_range(active_param_range()); + + let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID); let mut popup = Popup::new(SignatureHelp::ID, contents) .position(old_popup.and_then(|p| p.get_position())) .position_bias(Open::Above) @@ -298,11 +264,11 @@ fn signature_help_post_insert_char_hook( let (view, doc) = current!(cx.editor); // TODO support multiple language servers (not just the first that is found), likely by merging UI somehow let Some(language_server) = doc - .language_servers_with_feature(LanguageServerFeature::SignatureHelp) - .next() - else { - return Ok(()); - }; + .language_servers_with_feature(LanguageServerFeature::SignatureHelp) + .next() + else { + return Ok(()); + }; let capabilities = language_server.capabilities(); @@ -353,7 +319,7 @@ pub(super) fn register_hooks(handlers: &Handlers) { let tx = handlers.signature_hints.clone(); register_hook!(move |event: &mut DocumentDidChange<'_>| { - if event.doc.config.load().lsp.auto_signature_help && !event.ghost_transaction { + if event.doc.config.load().lsp.auto_signature_help { send_blocking(&tx, SignatureHelpEvent::ReTrigger); } Ok(()) |