From aeaff26451ec9b230550a4f1855d731e719ca608 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Sat, 17 Jan 2026 19:46:07 -0800 Subject: [PATCH] Preserve slash command order in search (#9425) Keep slash popup search results in presentation order for built-ins and prompts. --- codex-rs/tui/src/bottom_pane/command_popup.rs | 44 ++++++++++++------- .../tui2/src/bottom_pane/command_popup.rs | 34 +++++++------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs index ec4e86af0..10a469c68 100644 --- a/codex-rs/tui/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui/src/bottom_pane/command_popup.rs @@ -113,8 +113,8 @@ impl CommandPopup { } /// Compute fuzzy-filtered matches over built-in commands and user prompts, - /// paired with optional highlight indices and score. Sorted by ascending - /// score, then by name for stability. + /// paired with optional highlight indices and score. Preserves the original + /// presentation order for built-ins and prompts. fn filtered(&self) -> Vec<(CommandItem, Option>, i32)> { let filter = self.command_filter.trim(); let mut out: Vec<(CommandItem, Option>, i32)> = Vec::new(); @@ -144,20 +144,6 @@ impl CommandPopup { out.push((CommandItem::UserPrompt(idx), Some(indices), score)); } } - // When filtering, sort by ascending score and then by name for stability. - out.sort_by(|a, b| { - a.2.cmp(&b.2).then_with(|| { - let an = match a.0 { - CommandItem::Builtin(c) => c.command(), - CommandItem::UserPrompt(i) => &self.prompts[i].name, - }; - let bn = match b.0 { - CommandItem::Builtin(c) => c.command(), - CommandItem::UserPrompt(i) => &self.prompts[i].name, - }; - an.cmp(bn) - }) - }); out } @@ -292,6 +278,32 @@ mod tests { } } + #[test] + fn filtered_commands_keep_presentation_order() { + let mut popup = CommandPopup::new(Vec::new(), false); + popup.on_composer_text_change("/m".to_string()); + + let cmds: Vec<&str> = popup + .filtered_items() + .into_iter() + .filter_map(|item| match item { + CommandItem::Builtin(cmd) => Some(cmd.command()), + CommandItem::UserPrompt(_) => None, + }) + .collect(); + assert_eq!( + cmds, + vec![ + "model", + "experimental", + "resume", + "compact", + "mention", + "mcp" + ] + ); + } + #[test] fn prompt_discovery_lists_custom_prompts() { let prompts = vec![ diff --git a/codex-rs/tui2/src/bottom_pane/command_popup.rs b/codex-rs/tui2/src/bottom_pane/command_popup.rs index 2fee23f1b..ba49ab12c 100644 --- a/codex-rs/tui2/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui2/src/bottom_pane/command_popup.rs @@ -113,8 +113,8 @@ impl CommandPopup { } /// Compute fuzzy-filtered matches over built-in commands and user prompts, - /// paired with optional highlight indices and score. Sorted by ascending - /// score, then by name for stability. + /// paired with optional highlight indices and score. Preserves the original + /// presentation order for built-ins and prompts. fn filtered(&self) -> Vec<(CommandItem, Option>, i32)> { let filter = self.command_filter.trim(); let mut out: Vec<(CommandItem, Option>, i32)> = Vec::new(); @@ -144,20 +144,6 @@ impl CommandPopup { out.push((CommandItem::UserPrompt(idx), Some(indices), score)); } } - // When filtering, sort by ascending score and then by name for stability. - out.sort_by(|a, b| { - a.2.cmp(&b.2).then_with(|| { - let an = match a.0 { - CommandItem::Builtin(c) => c.command(), - CommandItem::UserPrompt(i) => &self.prompts[i].name, - }; - let bn = match b.0 { - CommandItem::Builtin(c) => c.command(), - CommandItem::UserPrompt(i) => &self.prompts[i].name, - }; - an.cmp(bn) - }) - }); out } @@ -291,6 +277,22 @@ mod tests { } } + #[test] + fn filtered_commands_keep_presentation_order() { + let mut popup = CommandPopup::new(Vec::new(), false); + popup.on_composer_text_change("/m".to_string()); + + let cmds: Vec<&str> = popup + .filtered_items() + .into_iter() + .filter_map(|item| match item { + CommandItem::Builtin(cmd) => Some(cmd.command()), + CommandItem::UserPrompt(_) => None, + }) + .collect(); + assert_eq!(cmds, vec!["model", "resume", "compact", "mention", "mcp"]); + } + #[test] fn prompt_discovery_lists_custom_prompts() { let prompts = vec![