diff options
author | Gokul Soumya | 2022-07-19 02:28:24 +0000 |
---|---|---|
committer | GitHub | 2022-07-19 02:28:24 +0000 |
commit | 791bf7e50a19bcf7612788deb7514847089cb976 (patch) | |
tree | 0bac607be8b940aed8000b77a2f4dfa2e14882b8 /helix-term/src/ui/popup.rs | |
parent | 02f009921007301284cbb0db4bc36bc629088fbb (diff) |
Add lsp signature help (#1755)
* Add lsp signature help
* Do not move signature help popup on multiple triggers
* Highlight current parameter in signature help
* Auto close signature help
* Position signature help above to not block completion
* Update signature help on backspace/insert mode delete
* Add lsp.auto-signature-help config option
* Add serde default annotation for LspConfig
* Show LSP inactive message only if signature help is invoked manually
* Do not assume valid signature help response from LSP
Malformed LSP responses are common, and these should not crash the
editor.
* Check signature help capability before sending request
* Reuse Open enum for PositionBias in popup
* Close signature popup and exit insert mode on escape
* Add config to control signature help docs display
* Use new Margin api in signature help
* Invoke signature help on changing to insert mode
Diffstat (limited to 'helix-term/src/ui/popup.rs')
-rw-r--r-- | helix-term/src/ui/popup.rs | 59 |
1 files changed, 50 insertions, 9 deletions
diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index f5b79526..77ab2462 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -1,4 +1,5 @@ use crate::{ + commands::Open, compositor::{Callback, Component, Context, EventResult}, ctrl, key, }; @@ -17,8 +18,10 @@ pub struct Popup<T: Component> { margin: Margin, size: (u16, u16), child_size: (u16, u16), + position_bias: Open, scroll: usize, auto_close: bool, + ignore_escape_key: bool, id: &'static str, } @@ -29,15 +32,27 @@ impl<T: Component> Popup<T> { position: None, margin: Margin::none(), size: (0, 0), + position_bias: Open::Below, child_size: (0, 0), scroll: 0, auto_close: false, + ignore_escape_key: false, id, } } - pub fn set_position(&mut self, pos: Option<Position>) { + pub fn position(mut self, pos: Option<Position>) -> Self { self.position = pos; + self + } + + pub fn get_position(&self) -> Option<Position> { + self.position + } + + pub fn position_bias(mut self, bias: Open) -> Self { + self.position_bias = bias; + self } pub fn margin(mut self, margin: Margin) -> Self { @@ -50,6 +65,18 @@ impl<T: Component> Popup<T> { self } + /// Ignores an escape keypress event, letting the outer layer + /// (usually the editor) handle it. This is useful for popups + /// in insert mode like completion and signature help where + /// the popup is closed on the mode change from insert to normal + /// which is done with the escape key. Otherwise the popup consumes + /// the escape key event and closes it, and an additional escape + /// would be required to exit insert mode. + pub fn ignore_escape_key(mut self, ignore: bool) -> Self { + self.ignore_escape_key = ignore; + self + } + pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) { let position = self .position @@ -68,13 +95,23 @@ impl<T: Component> Popup<T> { rel_x = rel_x.saturating_sub((rel_x + width).saturating_sub(viewport.width)); } - // TODO: be able to specify orientation preference. We want above for most popups, below - // for menus/autocomplete. - if viewport.height > rel_y + height { - rel_y += 1 // position below point - } else { - rel_y = rel_y.saturating_sub(height) // position above point - } + let can_put_below = viewport.height > rel_y + height; + let can_put_above = rel_y.checked_sub(height).is_some(); + let final_pos = match self.position_bias { + Open::Below => match can_put_below { + true => Open::Below, + false => Open::Above, + }, + Open::Above => match can_put_above { + true => Open::Above, + false => Open::Below, + }, + }; + + rel_y = match final_pos { + Open::Above => rel_y.saturating_sub(height), + Open::Below => rel_y + 1, + }; (rel_x, rel_y) } @@ -112,9 +149,13 @@ impl<T: Component> Component for Popup<T> { _ => return EventResult::Ignored(None), }; + if key!(Esc) == key.into() && self.ignore_escape_key { + return EventResult::Ignored(None); + } + let close_fn: Callback = Box::new(|compositor, _| { // remove the layer - compositor.pop(); + compositor.remove(self.id.as_ref()); }); match key.into() { |