diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d545480b53085..78cb624328af7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -376,6 +376,8 @@ impl MappableCommand { extend_to_line_end, "Extend to line end", extend_to_line_end_newline, "Extend to line end", signature_help, "Show signature help", + signature_next, "Show next signature", + signature_previous, "Show previous signature", smart_tab, "Insert tab if all cursors have all whitespace to their left; otherwise, run a separate command.", insert_tab, "Insert tab char", insert_newline, "Insert newline char", @@ -5998,3 +6000,11 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { } jump_to_label(cx, words, behaviour) } + +fn signature_next(cx: &mut Context) { + cx.editor.handlers.next_signature_help(); +} + +fn signature_previous(cx: &mut Context) { + cx.editor.handlers.previous_signature_help(); +} diff --git a/helix-term/src/handlers/signature_help.rs b/helix-term/src/handlers/signature_help.rs index 3c746548ac8c4..7d0ba55159bb5 100644 --- a/helix-term/src/handlers/signature_help.rs +++ b/helix-term/src/handlers/signature_help.rs @@ -24,7 +24,7 @@ use crate::{job, ui}; #[derive(Debug)] enum State { - Open, + Open { signature_count: usize }, Closed, Pending { request: CancelTx }, } @@ -37,6 +37,7 @@ const TIMEOUT: u64 = 120; pub(super) struct SignatureHelpHandler { trigger: Option, state: State, + active_signature: Option, } impl SignatureHelpHandler { @@ -44,8 +45,41 @@ impl SignatureHelpHandler { SignatureHelpHandler { trigger: None, state: State::Closed, + active_signature: None, } } + + pub fn next_signature(&mut self) { + let State::Open { signature_count } = self.state else { + return; + }; + + let value = self + .active_signature + .map(|x| if x >= signature_count - 1 { 0 } else { x + 1 }) + .unwrap_or_default(); + + self.active_signature.replace(value); + } + + pub fn previous_signature(&mut self) { + let State::Open { signature_count } = self.state else { + return; + }; + + let value = self + .active_signature + .map(|x| if x == 0 { signature_count - 1 } else { x - 1 }) + .unwrap_or(signature_count - 1); + + self.active_signature.replace(value); + } + + /// set the state to closed and clear active_signature + fn close(&mut self) { + self.state = State::Closed; + self.active_signature = None; + } } impl helix_event::AsyncHook for SignatureHelpHandler { @@ -59,7 +93,7 @@ impl helix_event::AsyncHook for SignatureHelpHandler { match event { SignatureHelpEvent::Invoked => { self.trigger = Some(SignatureHelpInvoked::Manual); - self.state = State::Closed; + self.close(); self.finish_debounce(); return None; } @@ -71,19 +105,35 @@ impl helix_event::AsyncHook for SignatureHelpHandler { } } SignatureHelpEvent::Cancel => { - self.state = State::Closed; + self.close(); return None; } - SignatureHelpEvent::RequestComplete { open } => { + SignatureHelpEvent::RequestComplete { + open, + signature_count, + active_signature, + } => { // don't cancel rerequest that was already triggered if let State::Pending { request } = &self.state { if !request.is_closed() { return timeout; } } - self.state = if open { State::Open } else { State::Closed }; + if open { + if self.active_signature.is_none() + || self.active_signature.unwrap() >= signature_count + { + self.active_signature = active_signature; + } + self.state = State::Open { signature_count }; + } else { + self.close(); + } + return timeout; } + SignatureHelpEvent::Next => self.next_signature(), + SignatureHelpEvent::Previous => self.previous_signature(), } if self.trigger.is_none() { self.trigger = Some(SignatureHelpInvoked::Automatic) @@ -95,7 +145,11 @@ impl helix_event::AsyncHook for SignatureHelpHandler { let invocation = self.trigger.take().unwrap(); let (tx, rx) = cancelation(); self.state = State::Pending { request: tx }; - job::dispatch_blocking(move |editor, _| request_signature_help(editor, invocation, rx)) + let active_signature = self.active_signature; + + job::dispatch_blocking(move |editor, _| { + request_signature_help(editor, invocation, rx, active_signature) + }) } } @@ -103,6 +157,7 @@ pub fn request_signature_help( editor: &mut Editor, invoked: SignatureHelpInvoked, cancel: CancelRx, + signature_index: Option, ) { let (view, doc) = current!(editor); @@ -128,7 +183,7 @@ pub fn request_signature_help( match cancelable_future(future, cancel).await { Some(Ok(res)) => { job::dispatch(move |editor, compositor| { - show_signature_help(editor, compositor, invoked, res) + show_signature_help(editor, compositor, invoked, res, signature_index) }) .await } @@ -143,6 +198,7 @@ pub fn show_signature_help( compositor: &mut Compositor, invoked: SignatureHelpInvoked, response: Option, + signature_index: Option, ) { let config = &editor.config(); @@ -170,7 +226,11 @@ pub fn show_signature_help( _ => { send_blocking( &editor.handlers.signature_hints, - SignatureHelpEvent::RequestComplete { open: false }, + SignatureHelpEvent::RequestComplete { + open: false, + signature_count: 0, + active_signature: None, + }, ); compositor.remove(SignatureHelp::ID); return; @@ -178,16 +238,20 @@ pub fn show_signature_help( }; send_blocking( &editor.handlers.signature_hints, - SignatureHelpEvent::RequestComplete { open: true }, + SignatureHelpEvent::RequestComplete { + open: true, + signature_count: response.signatures.len(), + active_signature: response.active_signature.map(|x| x as usize), + }, ); let doc = doc!(editor); let language = doc.language_name().unwrap_or(""); - let signature = match response - .signatures - .get(response.active_signature.unwrap_or(0) as usize) - { + let signature_index = + signature_index.unwrap_or_else(|| response.active_signature.unwrap_or_default() as usize); + + let signature = match response.signatures.get(signature_index) { Some(s) => s, None => return, }; @@ -197,6 +261,14 @@ pub fn show_signature_help( Arc::clone(&editor.syn_loader), ); + if response.signatures.len() > 1 { + contents.set_signature_index(format!( + "{}/{}", + signature_index + 1, + response.signatures.len() + )); + } + let signature_doc = if config.lsp.display_signature_help_docs { signature.documentation.as_ref().map(|doc| match doc { lsp::Documentation::String(s) => s.clone(), diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index ca5a21d26a164..61e95d4c9e0dc 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -389,6 +389,9 @@ pub fn default() -> HashMap { "pagedown" => page_down, "home" => goto_line_start, "end" => goto_line_end_newline, + + "S-down" => signature_next, + "S-up" => signature_previous, }); hashmap!( Mode::Normal => normal, diff --git a/helix-term/src/ui/lsp.rs b/helix-term/src/ui/lsp.rs index a3698e38d8795..0b19f62dbaaa3 100644 --- a/helix-term/src/ui/lsp.rs +++ b/helix-term/src/ui/lsp.rs @@ -15,6 +15,7 @@ use super::Popup; pub struct SignatureHelp { signature: String, signature_doc: Option, + signature_index: Option, /// Part of signature text active_param_range: Option<(usize, usize)>, @@ -36,6 +37,7 @@ impl SignatureHelp { active_param_range: None, language, config_loader, + signature_index: None, } } @@ -50,6 +52,10 @@ impl SignatureHelp { pub fn visible_popup(compositor: &mut Compositor) -> Option<&mut Popup> { compositor.find_id::>(Self::ID) } + + pub fn set_signature_index(&mut self, signature_index: String) { + self.signature_index = Some(signature_index); + } } impl Component for SignatureHelp { @@ -74,6 +80,13 @@ impl Component for SignatureHelp { active_param_span, ); + if let Some(signature_index) = &self.signature_index { + let sig_index = Markdown::new(signature_index.clone(), self.config_loader.clone()); + let sig_index = sig_index.parse(Some(&cx.editor.theme)); + let sig_index_para = Paragraph::new(&sig_index); + sig_index_para.render(area.with_height(1).clip_left(1), surface); + } + let (_, sig_text_height) = crate::ui::text::required_size(&sig_text, area.width); let sig_text_area = area.clip_top(1).with_height(sig_text_height); let sig_text_area = sig_text_area.inner(&margin).intersection(surface.area); diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index 724e7b1921caf..265eda36b273a 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -38,4 +38,12 @@ impl Handlers { }; send_blocking(&self.signature_hints, event) } + + pub fn next_signature_help(&self) { + send_blocking(&self.signature_hints, lsp::SignatureHelpEvent::Next) + } + + pub fn previous_signature_help(&self) { + send_blocking(&self.signature_hints, lsp::SignatureHelpEvent::Previous) + } } diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index beb106b2bf83e..de036e08f2125 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -41,7 +41,13 @@ pub enum SignatureHelpEvent { Trigger, ReTrigger, Cancel, - RequestComplete { open: bool }, + RequestComplete { + open: bool, + signature_count: usize, + active_signature: Option, + }, + Next, + Previous, } #[derive(Debug)]