From 057250020a7bdc75b74132010fd178e4569efd7a Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 10 Dec 2025 22:42:45 -0600 Subject: [PATCH] Fixed regression that broke fuzzy matching for slash commands (#7859) This addresses bug #7857 which was introduced recently as part of PR #7704. --- codex-rs/tui/src/bottom_pane/chat_composer.rs | 16 ++++++++++++---- codex-rs/tui/src/bottom_pane/command_popup.rs | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 39b600b15..919866b00 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -43,6 +43,7 @@ use crate::render::renderable::Renderable; use crate::slash_command::SlashCommand; use crate::slash_command::built_in_slash_commands; use crate::style::user_message_style; +use codex_common::fuzzy_match::fuzzy_match; use codex_protocol::custom_prompts::CustomPrompt; use codex_protocol::custom_prompts::PROMPTS_CMD_PREFIX; @@ -1621,7 +1622,7 @@ impl ChatComposer { let builtin_match = built_in_slash_commands() .into_iter() - .any(|(cmd_name, _)| cmd_name.starts_with(name)); + .any(|(cmd_name, _)| fuzzy_match(cmd_name, name).is_some()); if builtin_match { return true; @@ -1630,7 +1631,7 @@ impl ChatComposer { let prompt_prefix = format!("{PROMPTS_CMD_PREFIX}:"); self.custom_prompts .iter() - .any(|p| format!("{prompt_prefix}{}", p.name).starts_with(name)) + .any(|p| fuzzy_match(&format!("{prompt_prefix}{}", p.name), name).is_some()) } /// Synchronize `self.command_popup` with the current text in the @@ -3982,8 +3983,15 @@ mod tests { "'/re' should activate slash popup via prefix match" ); - // Case 3: invalid prefix "/zzz" – still allowed to open popup if it - // matches no built-in command, our current logic will *not* open popup. + // Case 3: fuzzy match "/ac" (subsequence of /compact and /feedback) + composer.set_text_content("/ac".to_string()); + assert!( + matches!(composer.active_popup, ActivePopup::Command(_)), + "'/ac' should activate slash popup via fuzzy match" + ); + + // Case 4: invalid prefix "/zzz" – still allowed to open popup if it + // matches no built-in command; our current logic will not open popup. // Verify that explicitly. composer.set_text_content("/zzz".to_string()); assert!( diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs index 8aca5c4a6..e1e35ae94 100644 --- a/codex-rs/tui/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui/src/bottom_pane/command_popup.rs @@ -373,4 +373,23 @@ mod tests { let description = rows.first().and_then(|row| row.description.as_deref()); assert_eq!(description, Some("send saved prompt")); } + + #[test] + fn fuzzy_filter_matches_subsequence_for_ac() { + let mut popup = CommandPopup::new(Vec::new(), false); + popup.on_composer_text_change("/ac".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!( + cmds.contains(&"compact") && cmds.contains(&"feedback"), + "expected fuzzy search for '/ac' to include compact and feedback, got {cmds:?}" + ); + } }