From eece782ff84a4e526c0b98b977547617b1db83c1 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 24 Aug 2024 10:57:25 -0400 Subject: [PATCH] Add config for keeping menu always open --- examples/ide_completions.rs | 7 ++++-- src/completion/default.rs | 10 ++++++++ src/engine.rs | 48 +++++++++++++++++++++++++++++++++---- src/enums.rs | 4 ++++ src/menu/mod.rs | 27 +++++++++++++++++++++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/examples/ide_completions.rs b/examples/ide_completions.rs index a209a167..6fcced45 100644 --- a/examples/ide_completions.rs +++ b/examples/ide_completions.rs @@ -17,7 +17,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) { KeyCode::Tab, ReedlineEvent::UntilFound(vec![ ReedlineEvent::Menu("completion_menu".to_string()), - ReedlineEvent::MenuNext, + ReedlineEvent::MenuAccept, ]), ); keybindings.add_binding( @@ -96,7 +96,10 @@ fn main() -> io::Result<()> { .with_min_description_width(min_description_width) .with_max_description_width(max_description_width) .with_description_offset(description_offset) - .with_correct_cursor_pos(correct_cursor_pos); + .with_correct_cursor_pos(correct_cursor_pos) + .with_activate_on_start(true) + .with_keep_active_after_accept(true) + .with_treat_submit_as_accept(false); if border { ide_menu = ide_menu.with_default_border(); diff --git a/src/completion/default.rs b/src/completion/default.rs index 882debb6..cd51e87d 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -118,6 +118,16 @@ impl Completer for DefaultCompleter { } } } + } else { + let span = Span::new(0, 0); + completions.extend(self.root.collect("").into_iter().map(|value| Suggestion { + value, + description: None, + style: None, + extra: None, + span, + append_whitespace: false, + })); } completions.dedup(); completions diff --git a/src/engine.rs b/src/engine.rs index 3c4fa2a0..78bd4128 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -516,6 +516,9 @@ impl Reedline { #[must_use] pub fn with_menu(mut self, menu: ReedlineMenu) -> Self { self.menus.push(menu); + if self.active_menu().is_none() { + self.activate_menus_on_start(); + } self } @@ -915,6 +918,7 @@ impl Reedline { | ReedlineEvent::HistoryHintWordComplete | ReedlineEvent::OpenEditor | ReedlineEvent::Menu(_) + | ReedlineEvent::MenuAccept | ReedlineEvent::MenuNext | ReedlineEvent::MenuPrevious | ReedlineEvent::MenuUp @@ -965,6 +969,24 @@ impl Reedline { } Ok(EventStatus::Inapplicable) } + ReedlineEvent::MenuAccept => { + match self.menus.iter_mut().find(|menu| menu.is_active()) { + None => Ok(EventStatus::Inapplicable), + Some(menu) => { + menu.replace_in_buffer(&mut self.editor); + if !menu.settings().keep_active_after_accept { + menu.menu_event(MenuEvent::Deactivate); + } else { + menu.update_values( + &mut self.editor, + self.completer.as_mut(), + self.history.as_ref(), + ); + } + Ok(EventStatus::Handled) + } + } + } ReedlineEvent::MenuNext => match self.active_menu() { None => Ok(EventStatus::Inapplicable), Some(menu) => { @@ -1083,13 +1105,23 @@ impl Reedline { Ok(EventStatus::Handled) } ReedlineEvent::Enter | ReedlineEvent::Submit | ReedlineEvent::SubmitOrNewline - if self.menus.iter().any(|menu| menu.is_active()) => + if self + .menus + .iter() + .any(|menu| menu.is_active() && menu.settings().treat_submit_as_accept) => { for menu in self.menus.iter_mut() { - if menu.is_active() { + if menu.is_active() && menu.settings().treat_submit_as_accept { menu.replace_in_buffer(&mut self.editor); - menu.menu_event(MenuEvent::Deactivate); - + if !menu.settings().keep_active_after_accept { + menu.menu_event(MenuEvent::Deactivate); + } else { + menu.update_values( + &mut self.editor, + self.completer.as_mut(), + self.history.as_ref(), + ); + } return Ok(EventStatus::Handled); } } @@ -1271,6 +1303,13 @@ impl Reedline { .for_each(|menu| menu.menu_event(MenuEvent::Deactivate)); } + fn activate_menus_on_start(&mut self) { + self.menus + .iter_mut() + .filter(|menu| menu.settings().activate_on_start) + .for_each(|menu| menu.menu_event(MenuEvent::Activate(false))); + } + fn previous_history(&mut self) { if self.history_cursor_on_excluded { self.history_cursor_on_excluded = false; @@ -1875,6 +1914,7 @@ impl Reedline { } self.run_edit_commands(&[EditCommand::Clear]); self.editor.reset_undo_stack(); + self.activate_menus_on_start(); Ok(EventStatus::Exits(Signal::Success(buffer))) } diff --git a/src/enums.rs b/src/enums.rs index b56896be..2d2df83e 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -615,6 +615,9 @@ pub enum ReedlineEvent { /// Trigger a menu event. It activates a menu with the event name Menu(String), + /// Accepts the current menu suggestion + MenuAccept, + /// Next element in the menu MenuNext, @@ -677,6 +680,7 @@ impl Display for ReedlineEvent { ReedlineEvent::Multiple(_) => write!(f, "Multiple[ {{ ReedLineEvents, }} ]"), ReedlineEvent::UntilFound(_) => write!(f, "UntilFound [ {{ ReedLineEvents, }} ]"), ReedlineEvent::Menu(_) => write!(f, "Menu Name: "), + ReedlineEvent::MenuAccept => write!(f, "MenuAccept"), ReedlineEvent::MenuNext => write!(f, "MenuNext"), ReedlineEvent::MenuPrevious => write!(f, "MenuPrevious"), ReedlineEvent::MenuUp => write!(f, "MenuUp"), diff --git a/src/menu/mod.rs b/src/menu/mod.rs index a08ccbc0..7abe5684 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -160,6 +160,9 @@ pub struct MenuSettings { /// Calls the completer using only the line buffer difference difference /// after the menu was activated only_buffer_difference: bool, + pub(crate) activate_on_start: bool, + pub(crate) keep_active_after_accept: bool, + pub(crate) treat_submit_as_accept: bool, } impl Default for MenuSettings { @@ -169,6 +172,9 @@ impl Default for MenuSettings { color: MenuTextStyle::default(), marker: "| ".to_string(), only_buffer_difference: false, + activate_on_start: false, + keep_active_after_accept: false, + treat_submit_as_accept: true, } } } @@ -268,6 +274,27 @@ pub trait MenuBuilder: Menu + Sized { self.settings_mut().only_buffer_difference = only_buffer_difference; self } + + /// Menu builder with new value for activate_on_start + #[must_use] + fn with_activate_on_start(mut self, activate_on_start: bool) -> Self { + self.settings_mut().activate_on_start = activate_on_start; + self + } + + /// Menu builder with new value for keep_active_after_accept + #[must_use] + fn with_keep_active_after_accept(mut self, keep_active_after_accept: bool) -> Self { + self.settings_mut().keep_active_after_accept = keep_active_after_accept; + self + } + + /// Menu builder with new value for treat_submit_as_accept + #[must_use] + fn with_treat_submit_as_accept(mut self, treat_submit_as_accept: bool) -> Self { + self.settings_mut().treat_submit_as_accept = treat_submit_as_accept; + self + } } /// Allowed menus in Reedline