feat(desktop): enable fuzzy @-search with directory support in Composer#3846
Merged
Conversation
The v2 @-menu search matched only on the basename of each file, so an input like "@planind" never surfaced a deeply-nested match such as "src/planind/index.tsx" whose basename is "index.tsx". * internal/fileref/search.go: split matches into a basename tier and a path-segment tier; basename hits still win, but the segment tier now fills the remaining capacity using pathSegmentContains(rel, query). Noise directories (node_modules, build, dist, .git, ...) are still skipped before the matching switch. * internal/fileref/search_test.go: cover path-segment match, basename match, sort order between tiers, and noise-directory skip list. * desktop/frontend/src/lib/atMatches.ts: extract the Composer @-menu filter into a pure helper so it can be unit-tested without React. * desktop/frontend/src/__tests__/at-matches.test.ts: regression tests for nested path surfacing, basename match, and stable de-duplication. * desktop/frontend/src/components/Composer.tsx: delegate the filter to the helper. The atDir !== "" guard for sub-path search is intentionally left unchanged for this fix.
esengine
approved these changes
Jun 11, 2026
esengine
left a comment
Owner
There was a problem hiding this comment.
Reviewed. The three-tier match (basename → path-segment → directory) with a small directory quota is the right ranking, and the switch in the walk callback means tier 2 is effectively "non-basename segments only" — no double counting. Dropping the early SkipAll is the necessary cost of tiering and the walk stays bounded by maxWalkEntries. Propagating IsDir out of SearchFileRefs fixes a genuine pre-existing bug, and the pure filterAtMatches helper with its own test file is exactly how I want Composer logic factored. Both test suites are meaningful. Merging, thanks!
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.
Summary
Restore the v1-style fuzzy
@filesearch behavior in the Composer so thatan intermediate directory segment in a query (e.g.
@planind) surfacesnested matches such as
src/planind/index.tsx, and directory names likeassets/are selectable from the @-menu.Previously the backend matched only on
d.Name()(basename), the frontendfilter reduced
searchEntriesto basename-only, andSearchFileRefshardcoded
IsDir: falsefor all results — so deep paths and directorieswere invisible to the @-menu.
Closes #3769.
What changed
internal/fileref/search.goSearchnow returns[]SearchResult(withPathandIsDir) instead of[]string.segment hits (fallback), and directory-name hits (lowest). A
dirQuotaof 5 ensures directories are never fully crowded out by file matches.
pathSegmentContains(relSlash, queryLower)checks any slash-separatedpath segment for a match.
.git,node_modules,build,dist, ...) arestill skipped. The public API signature is unchanged.
desktop/app.goSearchFileRefsnow propagatesr.IsDirfromSearchResulttoDirEntry.IsDir, fixing a pre-existing bug where all results werehardcoded
IsDir: false.internal/cli/complete.go[]SearchResultreturn type (CLI only usesPath).desktop/frontend/src/lib/atMatches.ts(new)filterAtMatches(entries, searchEntries, atFrag)thatmatches against the full slash-normalized relative path and de-duplicates
by
e.namewith the local list taking precedence.desktop/frontend/src/__tests__/at-matches.test.ts(new)local-before-fuzzy ordering, and the empty-fragment legacy behavior.
desktop/frontend/src/components/Composer.tsxatMatchesuseMemonow delegates tofilterAtMatches. The restof the @-menu logic, the
atDir !== ""guard, and the directorynavigation UX are intentionally untouched.
internal/fileref/search_test.go(new)with
IsDir=true, sort order between tiers, and noise-directory skip.Test results
go test ./internal/fileref -count=1— 6/6 PASSpnpm exec tsx src/__tests__/at-matches.test.ts— 5/5 PASSgo build ./...— clean