chore: unify filter mode & squeeze more perf#974
Merged
LoricAndre merged 6 commits intomasterfrom Feb 15, 2026
Merged
Conversation
Remove the standalone `filter` function from main.rs and integrate filter mode into the main `run_with` pipeline. When `options.filter` is set, `should_enter` now waits for all items to be processed and returns false (skipping TUI), and `App::results` returns all matched items. This unifies filter mode with the rest of the codebase so it benefits from all other flags (sorting, tiebreaks, etc.). https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw
Three changes that eliminate the performance regression from routing filter mode through run_with: 1. should_enter(): Wait for reader to finish BEFORE starting matcher, then run matcher exactly once. The old polling loop called restart_matcher() repeatedly, each time resetting the ItemPool taken counter and re-processing all items from scratch. With 1M items arriving in batches, items were matched multiple times. 2. matcher.run(): Remove unnecessary .enumerate() (index was discarded) and remove item.clone() — into_par_iter() yields owned values so the Arc can be moved directly into MatchedItem. 3. App::results(): In filter mode, drain items instead of cloning to avoid 271K MatchedItem clones + Arc allocations. Benchmark (1M file paths, query "test", 10 runs): Old standalone filter: 5.306s ± 0.085s New unified filter: 4.932s ± 0.099s (1.08x faster) https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw
When force=false, skip the item pool reset so take() only returns new (untaken) items. The callback merges new sorted matches into the existing sorted result list using an O(n+m) merge, instead of replacing it. This fixes the root cause: previously every restart_matcher call re-processed ALL items from scratch because reset() set the taken counter to 0. This benefits all polling callers (filter, select-1, exit-0, sync), not just filter mode. Items arriving in batches are now each matched exactly once, and matching overlaps with I/O since batches are processed as they arrive. When force=true (query changed), behavior is unchanged: full reset and re-match. Reverts the filter-specific workaround from the previous commit in favor of this general fix; the original polling loop in should_enter is now efficient. Benchmark (1M file paths, query "test", 10 runs): Old standalone filter (master): 4.566s ± 0.055s Incremental restart_matcher: 3.654s ± 0.035s (1.25x faster) https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw
Move the two-sorted-list merge from the restart_matcher closure into MatchedItem::sorted_merge() for clarity and reusability. The method merges two Vec<MatchedItem> lists that are already sorted by rank into a single sorted Vec in O(n+m) time. https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw
When restart_matcher runs with force=false, the callback marks results with a MergeStrategy so the render loop knows how to combine them with existing items. Previously, render's .take() would drain processed_items to None, and the next incremental callback would create a fresh batch — causing the render to replace all items with just the latest batch, losing previously matched items. Now: - Replace: full re-match (force=true), replaces item list entirely - SortedMerge: incremental sorted results, merged into item_list.items - Append: incremental unsorted results (--no-sort), appended This fixes match count consistency in interactive mode. With 1M items and query "test", match count is now 290,083 on every run (matching fzf's consistency), vs wildly varying counts before (min 13, max 56,950). https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Benches
FZF
Current
Filter mode (which this PR make relevant)
Checklist
README.md, comments,src/manpage.rsand/orsrc/options.rsif applicable)Description of the changes
Note: codecov runs on the PR on this repo, but feel free to ignore it.