From e20024f0f04e3566e162947449d508111f1bcc13 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Fri, 2 Jan 2026 10:36:46 +0100 Subject: [PATCH] reader rls: minimize state for tracking the completion pager --- src/reader/reader.rs | 48 +++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/reader/reader.rs b/src/reader/reader.rs index a3808b59e..20d601474 100644 --- a/src/reader/reader.rs +++ b/src/reader/reader.rs @@ -560,6 +560,12 @@ enum JumpPrecision { To, } +#[derive(Copy, Clone, Eq, PartialEq)] +enum CompletionAction { + ShownAmbiguous, + InsertedUnique, +} + /// readline_loop_state_t encapsulates the state used in a readline loop. struct ReadlineLoopState { /// The last command that was executed. @@ -569,10 +575,7 @@ struct ReadlineLoopState { yank_len: usize, /// If the last "complete" readline command has inserted text into the command line. - complete_did_insert: bool, - - /// List of completions. - comp: Vec, + completion_action: Option, /// Whether the loop has finished, due to reaching the character limit or through executing a /// command. @@ -587,8 +590,7 @@ fn new() -> Self { Self { last_cmd: None, yank_len: 0, - complete_did_insert: true, - comp: vec![], + completion_action: Some(CompletionAction::InsertedUnique), finished: false, nchars: None, } @@ -2558,7 +2560,7 @@ fn handle_char_event(&mut self, injected_event: Option) -> ControlFlo if self.reset_loop_state { self.reset_loop_state = false; self.rls_mut().last_cmd = None; - self.rls_mut().complete_did_insert = false; + self.rls_mut().completion_action = None; } // Perhaps update the termsize. This is cheap if it has not changed. reader_update_termsize(self.parser); @@ -2920,7 +2922,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) { // but never complete{,_and_search}) // // Also paging is already cancelled above. - if self.rls().complete_did_insert + if self.rls().completion_action == Some(CompletionAction::InsertedUnique) && matches!( self.rls().last_cmd, Some(rl::Complete | rl::CompleteAndSearch) @@ -2969,8 +2971,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) { return; } if self.is_navigating_pager_contents() - || (!self.rls().comp.is_empty() - && !self.rls().complete_did_insert + || (self.rls().completion_action == Some(CompletionAction::ShownAmbiguous) && self.rls().last_cmd == Some(rl::Complete)) { // The user typed complete more than once in a row. If we are not yet fully @@ -6590,8 +6591,7 @@ fn compute_and_apply_completions(&mut self, c: ReadlineCmd) { return; } ExpandResultCode::ok => { - self.rls_mut().comp.clear(); - self.rls_mut().complete_did_insert = false; + self.rls_mut().completion_action = None; self.push_edit( EditableLineTag::Commandline, Edit::new(token_range, wc_expanded), @@ -6604,12 +6604,11 @@ fn compute_and_apply_completions(&mut self, c: ReadlineCmd) { // up to the end of the token we're completing. let cmdsub = &el.text()[cmdsub_range.start..token_range.end]; - let (comp, _needs_load) = complete( + let (mut comp, _needs_load) = complete( cmdsub, CompletionRequestOptions::normal(), &self.parser.context(), ); - self.rls_mut().comp = comp; let el = &self.command_line; // User-supplied completions may have changed the commandline - prevent buffer @@ -6618,23 +6617,22 @@ fn compute_and_apply_completions(&mut self, c: ReadlineCmd) { token_range.end = std::cmp::min(token_range.end, el.text().len()); // Munge our completions. - sort_and_prioritize( - &mut self.rls_mut().comp, - CompletionRequestOptions::default(), - ); + sort_and_prioritize(&mut comp, CompletionRequestOptions::default()); let el = &self.command_line; // Record our cycle_command_line. self.cycle_command_line = el.text().to_owned(); self.cycle_cursor_pos = token_range.end; - self.rls_mut().complete_did_insert = self.handle_completions(token_range); + let inserted_unique = self.handle_completions(token_range, comp); + self.rls_mut().completion_action = if inserted_unique { + Some(CompletionAction::InsertedUnique) + } else { + (!self.pager.is_empty()).then_some(CompletionAction::ShownAmbiguous) + }; // Show the search field if requested and if we printed a list of completions. - if c == ReadlineCmd::CompleteAndSearch - && !self.rls().complete_did_insert - && !self.pager.is_empty() - { + if c == ReadlineCmd::CompleteAndSearch && !inserted_unique && !self.pager.is_empty() { self.pager.set_search_field_shown(true); self.select_completion_in_direction(SelectionMotion::Next, false); } @@ -6668,10 +6666,10 @@ fn try_insert(&mut self, c: Completion, tok: &wstr, token_range: Range) { /// \param token_end the position after the token to complete /// /// Return true if we inserted text into the command line, false if we did not. - fn handle_completions(&mut self, token_range: Range) -> bool { + fn handle_completions(&mut self, token_range: Range, comp: Vec) -> bool { let tok = self.command_line.text()[token_range.clone()].to_owned(); - let comp = &self.rls().comp; + let comp = ∁ // Check trivial cases. let len = comp.len(); if len == 0 {