diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 76e50229a295..d1543321dfbb 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -102,6 +102,14 @@ impl Menu { self.adjust_scroll(); } + pub fn move_half_page_up(&mut self) { + let len = self.matches.len(); + let max_index = len.saturating_sub((self.size.1 as usize / 2).max(1)); + let pos = self.cursor.map_or(max_index, |i| (i + max_index) % len) % len; + self.cursor = Some(pos); + self.adjust_scroll(); + } + pub fn move_down(&mut self) { let len = self.matches.len(); let pos = self.cursor.map_or(0, |i| i + 1) % len; @@ -109,6 +117,16 @@ impl Menu { self.adjust_scroll(); } + pub fn move_half_page_down(&mut self) { + let len = self.matches.len(); + let pos = self + .cursor + .map_or(0, |i| i + (self.size.1 as usize / 2).max(1)) + % len; + self.cursor = Some(pos); + self.adjust_scroll(); + } + fn recalculate_size(&mut self, viewport: (u16, u16)) { let n = self .options @@ -251,6 +269,18 @@ impl Component for Menu { (self.callback_fn)(cx.editor, self.selection(), MenuEvent::Update); return EventResult::Consumed(None); } + key!(PageUp) | ctrl!('u') => { + // page up moves back in the completion choice (including updating the doc) + self.move_half_page_up(); + (self.callback_fn)(cx.editor, self.selection(), MenuEvent::Update); + return EventResult::Consumed(None); + } + key!(PageDown) | ctrl!('d') => { + // page down advances completion choice (including updating the doc) + self.move_half_page_down(); + (self.callback_fn)(cx.editor, self.selection(), MenuEvent::Update); + return EventResult::Consumed(None); + } key!(Enter) => { if let Some(selection) = self.selection() { (self.callback_fn)(cx.editor, Some(selection), MenuEvent::Validate); diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index db77492db8d6..180c3040622f 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -270,34 +270,39 @@ impl Component for Popup { compositor.remove(self.id.as_ref()); }); - match key { - // esc or ctrl-c aborts the completion and closes the menu - key!(Esc) | ctrl!('c') => { - let _ = self.contents.handle_event(event, cx); - EventResult::Consumed(Some(close_fn)) - } - ctrl!('d') => { - self.scroll_half_page_down(); - EventResult::Consumed(None) - } - ctrl!('u') => { - self.scroll_half_page_up(); - EventResult::Consumed(None) - } - _ => { - let contents_event_result = self.contents.handle_event(event, cx); - - if self.auto_close { - if let EventResult::Ignored(None) = contents_event_result { - return EventResult::Ignored(Some(close_fn)); + // Code completion handles arrows and page up/down itself, + // but code lens does not. First check whether content knows + // about the key event. When not, check the default keys. + match self.contents.handle_event(event, cx) { + EventResult::Ignored(fn_once) => { + match key { + // esc or ctrl-c aborts the completion and closes the menu + key!(Esc) | ctrl!('c') => { + let _ = self.contents.handle_event(event, cx); + EventResult::Consumed(Some(close_fn)) + } + key!(PageDown) | ctrl!('d') => { + self.scroll_half_page_down(); + EventResult::Consumed(None) + } + key!(PageUp) | ctrl!('u') => { + self.scroll_half_page_up(); + EventResult::Consumed(None) + } + _ => { + // for some events, we want to process them but send ignore, specifically all input except + // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll. + + if self.auto_close { + EventResult::Ignored(Some(close_fn)) + } else { + EventResult::Ignored(fn_once) + } } } - - contents_event_result } + ev => ev, } - // for some events, we want to process them but send ignore, specifically all input except - // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll. } fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {