From 592703b857bd58c388d09046f571d29544b388d6 Mon Sep 17 00:00:00 2001 From: blindfs Date: Sat, 25 Oct 2025 18:30:38 +0800 Subject: [PATCH 1/4] feat: new `ReedlineEvent::PartialComplete` event --- src/engine.rs | 36 +++++++++++++++++++++--------------- src/enums.rs | 4 ++++ src/menu/columnar_menu.rs | 10 ++-------- src/menu/description_menu.rs | 1 - src/menu/ide_menu.rs | 10 ++-------- src/menu/list_menu.rs | 1 - src/menu/mod.rs | 15 +++++---------- 7 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 0c20861c3..a783a745f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1050,6 +1050,7 @@ impl Reedline { | ReedlineEvent::MenuRight | ReedlineEvent::MenuPageNext | ReedlineEvent::MenuPagePrevious + | ReedlineEvent::PartialComplete | ReedlineEvent::ViChangeMode(_) => Ok(EventStatus::Inapplicable), } } @@ -1060,6 +1061,22 @@ impl Reedline { event: ReedlineEvent, ) -> io::Result { match event { + ReedlineEvent::PartialComplete => { + let Some(menu) = self.menus.iter_mut().find(|menu| menu.is_active()) else { + return Ok(EventStatus::Inapplicable); + }; + if self.partial_completions + && menu.can_partially_complete( + &mut self.editor, + self.completer.as_mut(), + self.history.as_ref(), + ) + { + Ok(EventStatus::Handled) + } else { + Ok(EventStatus::Inapplicable) + } + } ReedlineEvent::Menu(name) => { if self.active_menu().is_none() { if let Some(menu) = self.menus.iter_mut().find(|menu| menu.name() == name) { @@ -1079,7 +1096,6 @@ impl Reedline { if self.partial_completions && menu.can_partially_complete( - self.quick_completions, &mut self.editor, self.completer.as_mut(), self.history.as_ref(), @@ -1093,26 +1109,17 @@ impl Reedline { } Ok(EventStatus::Inapplicable) } - ReedlineEvent::MenuNext => { - if let Some(menu) = self.menus.iter_mut().find(|menu| menu.is_active()) { + ReedlineEvent::MenuNext => match self.active_menu() { + None => Ok(EventStatus::Inapplicable), + Some(menu) => { if menu.get_values().len() == 1 && menu.can_quick_complete() { self.handle_editor_event(prompt, ReedlineEvent::Enter) } else { - if self.partial_completions { - menu.can_partially_complete( - self.quick_completions, - &mut self.editor, - self.completer.as_mut(), - self.history.as_ref(), - ); - } menu.menu_event(MenuEvent::NextElement); Ok(EventStatus::Handled) } - } else { - Ok(EventStatus::Inapplicable) } - } + }, ReedlineEvent::MenuPrevious => { self.active_menu() .map_or(Ok(EventStatus::Inapplicable), |menu| { @@ -1304,7 +1311,6 @@ impl Reedline { .handle_editor_event(prompt, ReedlineEvent::Enter); } else if self.partial_completions && menu.can_partially_complete( - self.quick_completions, &mut self.editor, self.completer.as_mut(), self.history.as_ref(), diff --git a/src/enums.rs b/src/enums.rs index bccaf96ab..f66f8af12 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -912,6 +912,9 @@ pub enum ReedlineEvent { /// Change mode (vi mode only) ViChangeMode(String), + + /// Complete the command given the common prefix of items in current open menu + PartialComplete, } impl Display for ReedlineEvent { @@ -960,6 +963,7 @@ impl Display for ReedlineEvent { ReedlineEvent::ExecuteHostCommand(_) => write!(f, "ExecuteHostCommand"), ReedlineEvent::OpenEditor => write!(f, "OpenEditor"), ReedlineEvent::ViChangeMode(_) => write!(f, "ViChangeMode mode: "), + ReedlineEvent::PartialComplete => write!(f, "PartialComplete"), } } } diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index 4e19514b3..0c6893f97 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -514,16 +514,9 @@ impl Menu for ColumnarMenu { /// in the given line buffer fn can_partially_complete( &mut self, - values_updated: bool, editor: &mut Editor, completer: &mut dyn Completer, ) -> bool { - // If the values were already updated (e.g. quick completions are true) - // there is no need to update the values from the menu - if !values_updated { - self.update_values(editor, completer); - } - if can_partially_complete(self.get_values(), editor) { // The values need to be updated because the spans need to be // recalculated for accurate replacement in the string @@ -784,7 +777,8 @@ mod tests { editor.set_buffer(input.to_string(), UndoBehavior::CreateUndoPoint); let mut completer = FakeCompleter::new(&$completions); - menu.can_partially_complete(false, &mut editor, &mut completer); + menu.update_values(&mut editor, &mut completer); + menu.can_partially_complete(&mut editor, &mut completer); assert_eq!(editor.get_buffer(), expected); } diff --git a/src/menu/description_menu.rs b/src/menu/description_menu.rs index a40552f21..ed8b521e4 100644 --- a/src/menu/description_menu.rs +++ b/src/menu/description_menu.rs @@ -419,7 +419,6 @@ impl Menu for DescriptionMenu { /// The menu does not need to partially complete fn can_partially_complete( &mut self, - _values_updated: bool, _editor: &mut Editor, _completer: &mut dyn Completer, ) -> bool { diff --git a/src/menu/ide_menu.rs b/src/menu/ide_menu.rs index 9fe29925e..1ba4dd7e7 100644 --- a/src/menu/ide_menu.rs +++ b/src/menu/ide_menu.rs @@ -580,16 +580,9 @@ impl Menu for IdeMenu { fn can_partially_complete( &mut self, - values_updated: bool, editor: &mut Editor, completer: &mut dyn Completer, ) -> bool { - // If the values were already updated (e.g. quick completions are true) - // there is no need to update the values from the menu - if !values_updated { - self.update_values(editor, completer); - } - if can_partially_complete(self.get_values(), editor) { // The values need to be updated because the spans need to be // recalculated for accurate replacement in the string @@ -1313,7 +1306,8 @@ mod tests { editor.set_buffer(input.to_string(), UndoBehavior::CreateUndoPoint); let mut completer = FakeCompleter::new(&$completions); - menu.can_partially_complete(false, &mut editor, &mut completer); + menu.update_values(&mut editor, &mut completer); + menu.can_partially_complete(&mut editor, &mut completer); assert_eq!(editor.get_buffer(), expected); } diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index 071d5ff5a..6d6dfda60 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -323,7 +323,6 @@ impl Menu for ListMenu { /// all registered values fn can_partially_complete( &mut self, - _values_updated: bool, _editor: &mut Editor, _completer: &mut dyn Completer, ) -> bool { diff --git a/src/menu/mod.rs b/src/menu/mod.rs index 8d1fc1652..1d43a10d1 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -106,7 +106,6 @@ pub trait Menu: Send { /// in the given line buffer fn can_partially_complete( &mut self, - values_updated: bool, editor: &mut Editor, completer: &mut dyn Completer, ) -> bool; @@ -306,23 +305,20 @@ impl ReedlineMenu { pub(crate) fn can_partially_complete( &mut self, - values_updated: bool, editor: &mut Editor, completer: &mut dyn Completer, history: &dyn History, ) -> bool { match self { - Self::EngineCompleter(menu) => { - menu.can_partially_complete(values_updated, editor, completer) - } + Self::EngineCompleter(menu) => menu.can_partially_complete(editor, completer), Self::HistoryMenu(menu) => { let mut history_completer = HistoryCompleter::new(history); - menu.can_partially_complete(values_updated, editor, &mut history_completer) + menu.can_partially_complete(editor, &mut history_completer) } Self::WithCompleter { menu, completer: own_completer, - } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()), + } => menu.can_partially_complete(editor, own_completer.as_mut()), } } @@ -399,18 +395,17 @@ impl Menu for ReedlineMenu { fn can_partially_complete( &mut self, - values_updated: bool, editor: &mut Editor, completer: &mut dyn Completer, ) -> bool { match self { Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => { - menu.can_partially_complete(values_updated, editor, completer) + menu.can_partially_complete(editor, completer) } Self::WithCompleter { menu, completer: own_completer, - } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()), + } => menu.can_partially_complete(editor, own_completer.as_mut()), } } From 995c0e96d1710f943e55582f69cbc15097f65316 Mon Sep 17 00:00:00 2001 From: blindfs Date: Sun, 26 Oct 2025 14:01:08 +0800 Subject: [PATCH 2/4] doc: TODO notes of lightweight span update for partial completion --- src/menu/columnar_menu.rs | 1 + src/menu/ide_menu.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index 0c6893f97..3e79211d5 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -520,6 +520,7 @@ impl Menu for ColumnarMenu { if can_partially_complete(self.get_values(), editor) { // The values need to be updated because the spans need to be // recalculated for accurate replacement in the string + // TODO: recalculate the spans instead of calling the completer 1 more time self.update_values(editor, completer); true diff --git a/src/menu/ide_menu.rs b/src/menu/ide_menu.rs index 1ba4dd7e7..2b9abfbe7 100644 --- a/src/menu/ide_menu.rs +++ b/src/menu/ide_menu.rs @@ -586,6 +586,7 @@ impl Menu for IdeMenu { if can_partially_complete(self.get_values(), editor) { // The values need to be updated because the spans need to be // recalculated for accurate replacement in the string + // TODO: recalculate the spans instead of calling the completer 1 more time self.update_values(editor, completer); true From a8e73a5e725489bb2a7d295d5222d96d4b67cce7 Mon Sep 17 00:00:00 2001 From: blindfs Date: Mon, 16 Feb 2026 19:49:45 +0800 Subject: [PATCH 3/4] refactor: ReedlineEvent reorder --- src/enums.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/enums.rs b/src/enums.rs index f66f8af12..5ac465fc7 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -904,6 +904,9 @@ pub enum ReedlineEvent { /// Move to the previous history page MenuPagePrevious, + /// Complete the command given the common prefix of items in current open menu + PartialComplete, + /// Way to bind the execution of a whole command (directly returning from [`crate::Reedline::read_line()`]) to a keybinding ExecuteHostCommand(String), @@ -912,9 +915,6 @@ pub enum ReedlineEvent { /// Change mode (vi mode only) ViChangeMode(String), - - /// Complete the command given the common prefix of items in current open menu - PartialComplete, } impl Display for ReedlineEvent { From c55d77cbaf0a2d76439b3f5fc347cd2a9d72f8b7 Mon Sep 17 00:00:00 2001 From: blindfs Date: Mon, 16 Feb 2026 20:16:36 +0800 Subject: [PATCH 4/4] fix: update_values for Menu event when `quick_completios` is set to false --- src/engine.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index a783a745f..41393cc71 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1094,14 +1094,19 @@ impl Reedline { } } - if self.partial_completions - && menu.can_partially_complete( + if self.partial_completions { + if !self.quick_completions { + menu.update_values( + &mut self.editor, + self.completer.as_mut(), + self.history.as_ref(), + ); + } + menu.can_partially_complete( &mut self.editor, self.completer.as_mut(), self.history.as_ref(), - ) - { - return Ok(EventStatus::Handled); + ); } return Ok(EventStatus::Handled);