Skip to content

Commit

Permalink
feat: cycle through function signatures/overloads
Browse files Browse the repository at this point in the history
implement handle_event to cycle through the function signatures.

by pressing up/down, ctrl + p/n or tab/shift+tab the function signature
will be changed. (similar behavior as the completion popup)

Signed-off-by: Ben Fekih, Hichem <[email protected]>
  • Loading branch information
karthago1 committed Apr 2, 2024
1 parent 190fbf6 commit b2065ac
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 15 deletions.
101 changes: 88 additions & 13 deletions helix-term/src/handlers/signature_help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{job, ui};

#[derive(Debug)]
enum State {
Open,
Open { signature_count: usize },
Closed,
Pending { request: CancelTx },
}
Expand All @@ -37,15 +37,49 @@ const TIMEOUT: u64 = 120;
pub(super) struct SignatureHelpHandler {
trigger: Option<SignatureHelpInvoked>,
state: State,
active_signature: Option<usize>,
}

impl SignatureHelpHandler {
pub fn new() -> SignatureHelpHandler {
SignatureHelpHandler {
trigger: None,
state: State::Closed,
active_signature: None,
}
}

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);
}

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 {
Expand All @@ -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;
}
Expand All @@ -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)
Expand All @@ -95,14 +145,19 @@ 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)
})
}
}

pub fn request_signature_help(
editor: &mut Editor,
invoked: SignatureHelpInvoked,
cancel: CancelRx,
signature_index: Option<usize>,
) {
let (view, doc) = current!(editor);

Expand All @@ -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
}
Expand All @@ -143,6 +198,7 @@ pub fn show_signature_help(
compositor: &mut Compositor,
invoked: SignatureHelpInvoked,
response: Option<lsp::SignatureHelp>,
signature_index: Option<usize>,
) {
let config = &editor.config();

Expand Down Expand Up @@ -170,33 +226,52 @@ 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;
}
};
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,
};
let mut contents = SignatureHelp::new(
signature.label.clone(),
language.to_string(),
Arc::clone(&editor.syn_loader),
|editor, event: SignatureHelpEvent| {
send_blocking(&editor.handlers.signature_hints, event);
},
);

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(),
Expand Down
40 changes: 39 additions & 1 deletion helix-term/src/ui/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ use std::sync::Arc;
use arc_swap::ArcSwap;
use helix_core::syntax;
use helix_view::graphics::{Margin, Rect, Style};
use helix_view::handlers::lsp::SignatureHelpEvent;
use helix_view::input::Event;
use helix_view::Editor;
use tui::buffer::Buffer;
use tui::widgets::{BorderType, Paragraph, Widget, Wrap};

use crate::compositor::{Component, Compositor, Context};
use crate::compositor::{Component, Compositor, Context, EventResult};

use crate::ui::Markdown;
use crate::{ctrl, key, shift};

use super::Popup;

pub struct SignatureHelp {
signature: String,
signature_doc: Option<String>,
signature_index: Option<String>,
/// Part of signature text
active_param_range: Option<(usize, usize)>,

language: String,
config_loader: Arc<ArcSwap<syntax::Loader>>,
callback: fn(&mut Editor, SignatureHelpEvent),
}

impl SignatureHelp {
Expand All @@ -29,13 +35,16 @@ impl SignatureHelp {
signature: String,
language: String,
config_loader: Arc<ArcSwap<syntax::Loader>>,
callback: fn(&mut Editor, SignatureHelpEvent),
) -> Self {
Self {
signature,
signature_doc: None,
active_param_range: None,
language,
config_loader,
signature_index: None,
callback,
}
}

Expand All @@ -50,9 +59,31 @@ impl SignatureHelp {
pub fn visible_popup(compositor: &mut Compositor) -> Option<&mut Popup<Self>> {
compositor.find_id::<Popup<Self>>(Self::ID)
}

pub fn set_signature_index(&mut self, signature_index: String) {
self.signature_index = Some(signature_index);
}
}

impl Component for SignatureHelp {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let Event::Key(event) = event else {
return EventResult::Ignored(None);
};

match event {
shift!(Tab) | key!(Up) | ctrl!('p') => {
(self.callback)(cx.editor, SignatureHelpEvent::Previous);
EventResult::Consumed(None)
}
key!(Tab) | key!(Down) | ctrl!('n') => {
(self.callback)(cx.editor, SignatureHelpEvent::Next);
EventResult::Consumed(None)
}
_ => EventResult::Ignored(None),
}
}

fn render(&mut self, area: Rect, surface: &mut Buffer, cx: &mut Context) {
let margin = Margin::horizontal(1);

Expand All @@ -74,6 +105,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);
Expand Down
8 changes: 7 additions & 1 deletion helix-view/src/handlers/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ pub enum SignatureHelpEvent {
Trigger,
ReTrigger,
Cancel,
RequestComplete { open: bool },
RequestComplete {
open: bool,
signature_count: usize,
active_signature: Option<usize>,
},
Next,
Previous,
}

#[derive(Debug)]
Expand Down

0 comments on commit b2065ac

Please sign in to comment.