Command search: setting to disable fuzzy matching, falling back to bash/zsh substring search (APP-5818) - #15818
Open
warp-agent-staging[bot] wants to merge 4 commits into
Open
Conversation
Adds a Terminal Input toggle that falls back to a literal, case-insensitive substring search (bash/zsh's reverse-history-search semantics) instead of Skim fuzzy matching, independent of HistorySearchRankingV2.
Contributor
Author
|
This PR was generated with Warp. Comment |
Verified each fix by deliberately reintroducing the regression it names (V2 path ignoring the setting, then legacy path ignoring it) and confirming only the matching test goes red, before reverting.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Command Search history results are fuzzy on every existing path: with
HistorySearchRankingV2on it's Skim subsequence matching plus whitespace AND-tokenization and the recency/session/exit priors; with the flag off it's still Skim subsequence matching against the whole query. Neither is what a shell does. bash (reverse-search-history) and zsh (history-incremental-search-backward) are literal substring containment, walked backwards from the most recent command, no ranking.This adds a "Fuzzy match command history in Command Search" toggle under Settings > Features > Terminal Input. When disabled, command history results fall back to that literal, no-ranking behavior; every other Command Search source (workflows, saved prompts, AI prompt history, env-var collections) keeps its own fuzzy matcher, unaffected by this setting.
Scope: history only, not the whole panel
The default Ctrl+R binding (
workspace:show_command_search) opens Command Search unfiltered, so in the default flow every other source sits in the same panel next to history. This PR intentionally leaves those sources untouched rather than suppressing them or extending literal matching to them — confirmed with the requester as the desired scope for now, with two consequences worth stating plainly rather than leaving for a reviewer to discover:Suppressing non-history sources when the setting is disabled — closer to what a real shell's Ctrl+R actually shows — was raised and rejected for this iteration; noting it here as the documented alternative if this needs revisiting.
Matching semantics
Disabled mode is literal substring containment, ASCII-case-insensitive, no subsequence matching, no whitespace tokenization (
cd hi ormno longer matchescd ~/projects/history_orm), no priors, ordered most-recent-first — matching bash/zsh's own behavior except for case-sensitivity (see below).Cross-source scoring
History's score is compared against every other Command Search source inside the mixer (
test_history_score_stays_comparable_to_other_sources_raw_skim_scalepins this for the enabled paths), so a mode that orders purely by recency can't just emit recency as the score without wrecking that comparability. Every literal-substring match instead gets the same score —literal_match_score(), derived from the query's own raw Skim self-match score (the theoretical ceiling for a query of that length) — so ties break via the mixer's stable sort, which preservesHistory::commands_shared()'s already-chronological candidate order. That's the same mechanism the existing blank-query zero-state bypass already relies on and tests; this just reuses it, gated on "is a literal substring match" instead of "query is blank."Using the full self-match value outright turned out to be wrong, caught before review: for query
"test", the self-match ceiling is91(raw Skim). AWorkflowmatch on its highest-weighted field (content, weighted0.5inFuzzyMatchWorkflowResult::score) tops out at91 * 0.5 = 45.5for the exact same query, matched exactly. Scoring every literal history match at the full91meant a history entry that merely contained"test"somewhere unrelated would tie the best possible match any workflow could ever produce for that query — a 2x, quality-blind advantage baked into the scoring, not something requested. The fix scales the shared score byLITERAL_MATCH_SCORE_FRACTION = 0.5, the same weightFuzzyMatchWorkflowResultalready gives its own top field, landing disabled-mode history in the same band a workflow's best match occupies rather than double it.disabled_fuzzy_matching_does_not_unconditionally_outrank_a_strong_competitorinsearcher_tests.rspins this: an exact-match AI prompt-history competitor now legitimately outranks a merely-containing history entry, which it could not before the fix.Deliberate case-insensitivity deviation
bash and zsh are case-sensitive by default, but Warp's search is case-insensitive throughout (readline itself ships
search-ignore-case), so the disabled mode stays case-insensitive rather than matching bash/zsh exactly.Command Palette and context flag
Per
AGENTS.md, added the matching Command Palette enable/disable entry ("fuzzy matching of command history in command search") and its context flag (COMMAND_SEARCH_FUZZY_MATCHING_FLAG), wired into the same keymap-context builder as the siblingInputSettingstoggles.Default is fuzzy enabled, so there's no behavior change for existing users.
Linked Issue
Linear: APP-5818
Testing
rank_tests.rs: literal substring matching is case-insensitive, doesn't match a fuzzy subsequence, doesn't tokenize a multi-word query, highlight indices span exactly the matched substring, blank query matches everything with no highlight, andliteral_match_scoreis identical across matches for the same query (half the query's self-match score) and scales with query length.searcher_tests.rs(7 new tests): disabled mode doesn't tokenize a multi-word query; short-circuits both the V2 and legacy ranking paths regardless ofHistorySearchRankingV2; orders results most-recent-first despite an unfavorable raw-Skim tiebreak (an exact match inserted before a merely-containing one); a literal match stays comparable to a weak fuzzy competitor from another source; a literal match does not unconditionally outrank a strong (exact-match) competitor from another source, proving the scoring fix; and the blank-query zero state still works when fuzzy matching is disabled.cargo test -p warp --lib search::command_searchandsettings_view(all passing),./script/format,cargo clippy -p warp --all-targets --tests -- -D warnings, the workspace-widecargo clippy --workspace --exclude warp_completer --all-targets --tests -- -D warnings, andcargo clippy -p warp_completer --all-targets --tests -- -D warnings(all clean).Visual verification is outstanding. An earlier computer-use pass captured screenshots against a commit that predates
a33dafada(the copy scoping fix below), so those captures showed stale wording and have been removed rather than left next to code they contradict. A recapture attempt against the current tip (039ad6338) confirmed the build was the correct internaldev.warp.WarpLocal(Channel::Local) binary, withwarp-channel-configcorrectly installed andWARP_API_KEYpresent and non-empty (no "provided but IGNORED" warning) — but the app nonetheless never advanced past the "Welcome to Warp" onboarding screen to an authenticated session. The blocker is credential acceptance in the cloud verification environment, not the build, the channel, or anything in this change. The rendered toggle copy, tokenized/literal matching behavior, and Command Palette entry have not been visually re-confirmed since the copy fix landed; that verification is still needed before merge.I have manually tested my changes locally with
./script/runAgent Mode