feat(desktop): Cmd+P と Files タブの検索ロジックを VSCode 準拠に刷新 (#359)#365
feat(desktop): Cmd+P と Files タブの検索ロジックを VSCode 準拠に刷新 (#359)#365
Conversation
右サイドバー Files タブと Cmd+P の「重い」「欲しいファイルが出ない」を VSCode Quick Open と同じ仕組みに寄せて改善 (#359)。 ## workspace-fs (core) - buildSearchIndex を ripgrep --files ベースに切替。.gitignore / .rgignore / .git/info/exclude を自動尊重するようになり、dist/build など gitignore されたビルド成果物はデフォルトで除外される - ripgrep 不在環境向けに fast-glob フォールバックを維持 - DEFAULT_IGNORE_PATTERNS を .git / node_modules の最小限に縮小 - searchFiles に openFilePaths / recentFilePaths を追加。Quick Open と同じく開いているファイルは +2000、MRU は +1000、さらに同スコア 時は recency 順に並べる range boost を実装 - searchFiles に AbortController ベースのキャンセル機構を追加し、 スコアリングループが中断できるよう yield ポイントを設置 - warmupSearchIndex API を追加し、初回 Cmd+P で ripgrep の初回 走査待ちが発生しないようにした - Fuse.js 依存とデッドコード (_collectExactFileSearchMatches 等) を削除 ## transport / UI - tRPC: searchFiles に新パラメータを足し、warmupSearchIndex プロシージャを追加 (desktop / host-service) - useFileSearch / useWorkspaceFileSearch の両方で MRU / include / exclude を使えるように feature parity を確保 - useCommandPalette で openFilePaths / recentFilePaths を受け取り searchFiles に転送。ダイアログ初回オープン時に warmup を発火 - v2-workspace page.tsx から絶対パスリストを useCommandPalette に 渡して MRU/open boost を有効化 - FileTreeToolbar の自前 debounce を削除し、hook の 150ms debounce に一本化 (二重 debounce を解消) ## tests - open file boost / MRU tiebreaker / warmup のテストを追加
📝 WalkthroughWalkthroughripgrepバイナリを統合し、ファイル検索にスコア付けの最適化を追加しました。 Changes
Sequence Diagram(s)sequenceDiagram
actor User as ユーザー
participant UI as CommandPalette UI
participant Hook as useCommandPalette
participant Search as FilesSearch
participant Index as SearchIndex
participant RG as Ripgrep
User->>UI: パレットを開く
UI->>Hook: useCommandPalette()
activate Hook
Hook->>Hook: パレットopen状態を検出
Hook->>Search: warmupSearchIndex mutation (workspaceId)
activate Search
Search->>Index: インデックス構築要求
activate Index
Index->>RG: ripgrep実行 (--files)
RG-->>Index: ファイルリスト返却
Index->>Index: スコア計算エントリを構築
Index-->>Search: インデックス準備完了
deactivate Index
Search-->>Hook: キャッシュ準備完了
deactivate Search
User->>UI: 検索テキスト入力
UI->>Hook: useFileSearch({query, openFilePaths, recentFilePaths})
activate Hook
Hook->>Search: searchFiles({query, openFilePaths, recentFilePaths, scopeId})
activate Search
Search->>Index: キャッシュからスコア計算開始
Search->>Search: MRUブースト適用 (recentFilePaths)
Search->>Search: オープンファイルブースト適用 (openFilePaths)
Search->>Search: スコアでソート
Search-->>Hook: 結果返却
deactivate Search
Hook-->>UI: 検索結果表示
deactivate Hook
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ed95e14dc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- ripgrep の `--follow=false` は不正なフラグ (exit 2) で、実際には ripgrep 経由の列挙が一度も動かず常に fast-glob にフォールバック していた。ripgrep はデフォルトで symlink を follow しないため、 このフラグ自体を削除して挙動を正しくする - スコアリングループが同期で、yield ポイントがフラグを見るだけで 実際には event loop に制御を返していなかった。setImmediate ベース の yieldToEventLoop ヘルパーを導入し、SCORE_YIELD_INTERVAL 件ごと に本当に macrotask をインターリーブする。後続の searchFiles 呼び出し が activeFileSearchControllers 経由で abort できるようになる - ripgrep 呼び出し時の引数をテストで検証するリグレッションテストを追加 Refs: #365 (Codex review comments)
## Patch event の .gitignore 整合性を回復 (High)
patchSearchIndexesForRoot が watch event からファイルを足す際、新 DEFAULT_IGNORE_PATTERNS (.git/ + node_modules/) しか弾いていなかったため、dist/ や .next/ のビルド成果物が visible index に混入していた。patchIgnoreMatchers を FALLBACK_IGNORE_PATTERNS ベースに戻し、ripgrep の full rebuild と同等以上の保守的な除外をかける。
## searchFiles に scopeId を追加 (High)
activeFileSearchControllers が rootPath のみで keying されていたため、同じ workspace の Cmd+P と Files タブが同時検索すると互いを abort しあっていた。SearchFilesOptions に scopeId を追加し、controllerKey を `${rootPath}::${scopeId}` に分離。useFileSearch は "files-tab"、useCommandPalette は "quick-open" / "quick-open-global" を渡す。
## Cold start もキャンセル可能に (Medium)
getSearchIndex() の完了までは AbortController が効かず、初回 rg 走査中のキーストロークが無駄になっていた。raceWithAbort ヘルパーで caller の signal を getSearchIndex の await に race させ、シェアされた build promise は殺さずに caller だけ短絡させる。
## warmup を毎オープンで発火 (Medium)
workspace ごとに warmedWorkspaceRef で 1 回に固定していたため、SEARCH_INDEX_TTL_MS (30s) 切れ後にダイアログを開くと cold start に戻っていた。ref を撤去し、毎オープンで mutation を発火。backend の inFlightBuilds と TTL により実質的な重複コストは無い。
## リグレッションテスト
- patch event が dist/ 配下のファイルを visible index に入れないこと
- scopeId を分けた同一 rootPath の並列 searchFiles が互いを潰さないこと
Refs: #365 (Codex review comments)
|
ローカルの Codex (gpt-5.4) に追加レビューを依頼し、以下4件を修正しました。 High
Medium
追加リグレッションテスト
意図通りとして見送った指摘
|
これまでの実装は `runRipgrep` がシェルの PATH 上の `rg` を呼んでいたため、
ユーザーが `brew install ripgrep` 等で入れていないと .gitignore 尊重の恩恵
が受けられず、VSCode Quick Open 準拠というこの PR のうたい文句が半分しか
届かない状態だった。
VSCode 本体と同じ `@vscode/ripgrep` パッケージを apps/desktop の依存に
追加し、配布物に ripgrep バイナリ本体 (platform 毎のプリビルド) を同梱する。
- apps/desktop/package.json に `@vscode/ripgrep` を追加
- root package.json の `trustedDependencies` にも追加し、bun install 時に
postinstall が走ってバイナリがダウンロードされるようにする
- runtime-dependencies.ts で externalizedRuntimeModules に登録し、
- mainExternalizedDependencies に自動追加 (electron-vite でバンドル
時に inline せず、node_modules から require させる)
- packagedNodeModuleCopies で配布物の node_modules に複製
- asarUnpackGlobs で asar 外に展開 (native binary は asar から exec
できないため)
- requiredMaterializedNodeModules でビルド時の同梱検証対象にする
- workspace-fs-service.ts の runRipgrep を `@vscode/ripgrep` の rgPath
ベースに差し替え。asar パスを asar.unpacked に書き換えるロジックも追加
これによりユーザー環境に rg が無くても Cmd+P / Files タブの .gitignore
尊重検索が動作する。ripgrep のバージョンも VSCode と同じ 15.0.0 に固定
されるので挙動差もなくなる。
以前は rg がどんなエラーを返しても fast-glob にサイレントフォールバック していたため、`--follow=false` 不正フラグのような実装バグが「動いているが .gitignore 非準拠」という壊れた状態で隠蔽されていた (最初のレビューで この挙動が実際に見逃されていた)。 - ENOENT (バイナリ未インストール) の場合だけ fallback 継続 → テスト環境や、apps/desktop 以外で workspace-fs を使う場合の互換用 - 他のエラー (exit 2, buffer overflow, permission denied など) は throw → 実装バグが即表面化する apps/desktop 本番は @vscode/ripgrep で rg を同梱しているので ENOENT は 発生せず、この分岐は事実上テスト用の safety net として残るだけ。 想定しない rg 失敗が throw になるリグレッションテストを追加。
CI の Build ジョブ (electron-builder) が以下で失敗していた:
⨯ Production dependency https-proxy-agent not found for package @vscode/ripgrep
⨯ production dependency not found parent=@vscode/ripgrep
dependency=https-proxy-agent version=^7.0.2
@vscode/ripgrep は `postinstall.js` / `download.js` 用に
`https-proxy-agent` / `yauzl` / `proxy-from-env` を production
dependencies として宣言している。electron-builder の bun 用
node_modules traversal collector はこれらを apps/desktop 側から
辿れないと配布物に含められずエラーになる。
runtime では `lib/index.js` が path.join するだけでこれらは使わないが、
package.json の宣言が正として扱われるため apps/desktop の
dependencies に明示的に追加して walker を満たす。
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/FilesPane/hooks/useWorkspaceFileSearch/useWorkspaceFileSearch.ts (1)
29-45:⚠️ Potential issue | 🟡 Minor異なる UI サーフェス間での searchFiles 衝突を防ぐため、
scopeIdを明示的に指定してください。Cmd+P は
"quick-open"、右サイドバー Files タブは"files-tab"を使用していますが、このコンポーネント(v2 workspace Files ペイン)ではscopeIdが指定されていません。同一のworkspaceIdに対して複数の UI サーフェスが同時に検索を実行する場合、backend 側でscopeIdをコントローラキーとしてキャッシング/abort 制御を行っているため、scopeId の未指定は他の検索リクエストの abort につながる可能性があります。"v2-files-pane"など固有の値を指定することで、並列検索が互いに干渉しないようになります。提案される修正
const { data: searchResults, isFetching } = workspaceTrpc.filesystem.searchFiles.useQuery( { workspaceId, query: debouncedQuery, limit, includePattern, excludePattern, openFilePaths, recentFilePaths, + scopeId: "v2-files-pane", },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/FilesPane/hooks/useWorkspaceFileSearch/useWorkspaceFileSearch.ts around lines 29 - 45, The search query for workspaceTrpc.filesystem.searchFiles.useQuery is missing a scopeId, which can cause cross-surface aborts; update the query input object passed to workspaceTrpc.filesystem.searchFiles.useQuery (the one that currently includes workspaceId, query: debouncedQuery, limit, includePattern, excludePattern, openFilePaths, recentFilePaths) to include a unique scopeId like "v2-files-pane" so this Files pane uses its own controller key and won’t interfere with other UI surfaces.
🧹 Nitpick comments (3)
packages/workspace-fs/src/search.test.ts (1)
361-389:expect(...).rejects.toThrowの使用を推奨。手動の
threwフラグよりも、bun:testの組み込みアサーションを使う方が意図が明確で、将来「例外は出たが別物だった」ケースも検知しやすくなります。♻️ 提案される修正
- let threw = false; - try { - await searchFiles({ - rootPath, - query: "alpha", - runRipgrep: async () => { - // Simulate the exact shape of an argv-parse error (rg exits 2 - // when it doesn't understand a flag). Pre-hardening, this - // failure silently degraded to fast-glob. - const error = new Error( - "Command failed: rg: unexpected argument for option '--follow'", - ) as Error & { code?: number }; - error.code = 2; - throw error; - }, - }); - } catch { - threw = true; - } - - expect(threw).toEqual(true); + await expect( + searchFiles({ + rootPath, + query: "alpha", + runRipgrep: async () => { + // Simulate the exact shape of an argv-parse error (rg exits 2 + // when it doesn't understand a flag). Pre-hardening, this + // failure silently degraded to fast-glob. + const error = new Error( + "Command failed: rg: unexpected argument for option '--follow'", + ) as Error & { code?: number }; + error.code = 2; + throw error; + }, + }), + ).rejects.toThrow(/--follow/);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workspace-fs/src/search.test.ts` around lines 361 - 389, Replace the manual try/catch + threw flag in the "surfaces unexpected ripgrep failures instead of silently falling back" test with bun:test's promise rejection assertion: call searchFiles(...) (with the runRipgrep override that throws the simulated Error) and assert await expect(searchFiles(...)).rejects.toThrow(...) so the test fails if the promise resolves or rejects with an unexpected value; reference the searchFiles call and the runRipgrep override when making the replacement.apps/desktop/src/renderer/screens/main/components/CommandPalette/useCommandPalette.ts (2)
211-215:query.trim()の重複計算。
query.trim()が同一式内で 2 回呼ばれています。読みやすさと微小なパフォーマンスのため、上部で一度キャプチャするとよりクリーンです(既に L173 でquery.trim()をuseDebouncedValueに渡しているので、trimmedQuery変数に束ねるとさらに良いです)。♻️ 提案される修正
- const debouncedQuery = useDebouncedValue(query.trim(), 150); + const trimmedQuery = query.trim(); + const debouncedQuery = useDebouncedValue(trimmedQuery, 150); @@ - const isFetching = - scope === "workspace" - ? singleSearch.isFetching - : multiSearchQueries.some((query) => query.isFetching) || - (query.trim().length > 0 && query.trim() !== debouncedQuery); + const isFetching = + scope === "workspace" + ? singleSearch.isFetching + : multiSearchQueries.some((q) => q.isFetching) || + (trimmedQuery.length > 0 && trimmedQuery !== debouncedQuery);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/CommandPalette/useCommandPalette.ts` around lines 211 - 215, isFetching computes query.trim() twice which is redundant; capture trimmedQuery once (reuse the existing trimmed value passed to useDebouncedValue or create a const trimmedQuery = query.trim()) and replace both occurrences in the isFetching expression (the conditional that references scope, singleSearch.isFetching, multiSearchQueries, query.trim(), and debouncedQuery) so the logic uses trimmedQuery instead of calling query.trim() multiple times.
132-156: sentinel 方式のメモ化はクレバーですが、意図を補足するとより堅牢です。
\u0000を区切り文字として join/split する方式は、配列内容が変わった場合のみ React Query が refetch するという目的を簡潔に達成しています。ただし、絶対パスに\u0000が含まれることは実質ないものの、将来的に他のデータを扱うよう拡張した際に trap になり得ます。現状は問題ありませんが、可能であればuseDeepCompareMemo的なユーティリティや、配列の同値比較を内部化した薄いカスタム hook に抽出するとより安全です(チル指摘)。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/CommandPalette/useCommandPalette.ts` around lines 132 - 156, The sentinel join/split using "\u0000" (computed into openFilePathsKey/recentFilePathsKey and split back into openFilePathsList/recentFilePathsList via useMemo) works but is fragile; replace it with a small, well-named utility hook (e.g. useDeepCompareMemo or useStableArrayKey) that performs a deep-equality comparison of the arrays and returns a stable value (or the original array) for use in React Query keys. Locate the current useMemo logic around openFilePathsKey, recentFilePathsKey, openFilePathsList and recentFilePathsList and extract/replace it with the new hook so memoization is based on deep array equality rather than a sentinel join/split; ensure the new hook preserves the same behavior (undefined vs [] handling) for downstream consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/workspace-fs/src/search.ts`:
- Around line 540-548: The current processing of ripgrep output in the block
using runRipgrep (with FILE_LISTING_RIPGREP_BUFFER_BYTES and cwd: rootPath)
improperly calls entry.trim(), which strips legitimate spaces in filenames;
change the return path to simply split by "\0" and filter out empty entries
(i.e., replace .split("\0").map(entry => entry.trim()).filter(entry =>
entry.length > 0) with .split("\0").filter(entry => entry.length > 0)) so
NUL-delimited filenames (including spaces) are preserved.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/FilesPane/hooks/useWorkspaceFileSearch/useWorkspaceFileSearch.ts:
- Around line 29-45: The search query for
workspaceTrpc.filesystem.searchFiles.useQuery is missing a scopeId, which can
cause cross-surface aborts; update the query input object passed to
workspaceTrpc.filesystem.searchFiles.useQuery (the one that currently includes
workspaceId, query: debouncedQuery, limit, includePattern, excludePattern,
openFilePaths, recentFilePaths) to include a unique scopeId like "v2-files-pane"
so this Files pane uses its own controller key and won’t interfere with other UI
surfaces.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/CommandPalette/useCommandPalette.ts`:
- Around line 211-215: isFetching computes query.trim() twice which is
redundant; capture trimmedQuery once (reuse the existing trimmed value passed to
useDebouncedValue or create a const trimmedQuery = query.trim()) and replace
both occurrences in the isFetching expression (the conditional that references
scope, singleSearch.isFetching, multiSearchQueries, query.trim(), and
debouncedQuery) so the logic uses trimmedQuery instead of calling query.trim()
multiple times.
- Around line 132-156: The sentinel join/split using "\u0000" (computed into
openFilePathsKey/recentFilePathsKey and split back into
openFilePathsList/recentFilePathsList via useMemo) works but is fragile; replace
it with a small, well-named utility hook (e.g. useDeepCompareMemo or
useStableArrayKey) that performs a deep-equality comparison of the arrays and
returns a stable value (or the original array) for use in React Query keys.
Locate the current useMemo logic around openFilePathsKey, recentFilePathsKey,
openFilePathsList and recentFilePathsList and extract/replace it with the new
hook so memoization is based on deep array equality rather than a sentinel
join/split; ensure the new hook preserves the same behavior (undefined vs []
handling) for downstream consumers.
In `@packages/workspace-fs/src/search.test.ts`:
- Around line 361-389: Replace the manual try/catch + threw flag in the
"surfaces unexpected ripgrep failures instead of silently falling back" test
with bun:test's promise rejection assertion: call searchFiles(...) (with the
runRipgrep override that throws the simulated Error) and assert await
expect(searchFiles(...)).rejects.toThrow(...) so the test fails if the promise
resolves or rejects with an unexpected value; reference the searchFiles call and
the runRipgrep override when making the replacement.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c7371af8-03e6-42d1-8192-083d166eeee9
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (18)
apps/desktop/package.jsonapps/desktop/runtime-dependencies.tsapps/desktop/src/lib/trpc/routers/filesystem/index.tsapps/desktop/src/lib/trpc/routers/workspace-fs-service.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/FilesPane/hooks/useWorkspaceFileSearch/useWorkspaceFileSearch.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsxapps/desktop/src/renderer/screens/main/components/CommandPalette/useCommandPalette.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeToolbar/FileTreeToolbar.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/hooks/useFileSearch/useFileSearch.tspackage.jsonpackages/host-service/src/trpc/router/filesystem/filesystem.tspackages/workspace-fs/package.jsonpackages/workspace-fs/src/bun-test.d.tspackages/workspace-fs/src/client/index.tspackages/workspace-fs/src/core/service.tspackages/workspace-fs/src/host/service.tspackages/workspace-fs/src/search.test.tspackages/workspace-fs/src/search.ts
| try { | ||
| const { stdout } = await runRipgrep(args, { | ||
| cwd: rootPath, | ||
| maxBuffer: FILE_LISTING_RIPGREP_BUFFER_BYTES, | ||
| }); | ||
| return stdout | ||
| .split("\0") | ||
| .map((entry) => entry.trim()) | ||
| .filter((entry) => entry.length > 0); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
ripgrep --files --null output format does it include trailing newline or only null byte separators
💡 Result:
The ripgrep rg --files --null (or -0) command outputs each file path followed immediately by a NUL byte (\0) separator, with no trailing newline after the final path. Paths are NUL-terminated but not newline-terminated, making the output suitable for tools like xargs -0 that expect NUL-delimited input without relying on newlines. This behavior is explicitly documented in multiple ripgrep man pages: "Whenever a file path is printed, follow it with a NUL byte. This includes printing file paths [...] and when printing a list of matching files such as with [...] --files." The --null flag replaces the default newline separator with NUL for file paths in --files mode. No sources indicate trailing newlines are added; the output uses only NUL bytes as separators, consistent with standard Unix tools like find -print0 or git ls-files -z. For example, issues discussing --null confirm it produces clean NUL-separated paths without extra newlines (e.g., intended for xargs -0 usage). If a trailing newline were present, it would break compatibility with NUL-processing tools, but documentation and usage examples affirm it does not.
Citations:
- 1: https://manpages.ubuntu.com/manpages/noble/man1/rg.1.html
- 2: https://manpages.debian.org/bullseye/ripgrep/rg.1.en.html
- 3: https://manpages.ubuntu.com/manpages/focal/en/man1/rg.1.html
- 4: https://leancrew.com/all-this/man/man1/rg.html
- 5: https://manpages.ubuntu.com/manpages/jammy/man1/rg.1.html
- 6: "--null" or "--print0" option? BurntSushi/ripgrep#89
- 7: rg --files -z ( \0 line termination) BurntSushi/ripgrep#1233
entry.trim() が NUL 区切り出力の保証を損なっています。
Ripgrep の --files --null は NUL バイト(\0)のみで分区し、末尾に改行を追加しません。したがって split("\0") だけで十分です。しかし entry.trim() は「ファイル名に含まれる正当なスペース」も削除してしまい、POSIX でスペースを含むファイル名の検索がスキップされたり不一致が起きる可能性があります。
.split("\0").filter(entry => entry.length > 0) に簡潔にしてください。
提案する修正
- return stdout
- .split("\0")
- .map((entry) => entry.trim())
- .filter((entry) => entry.length > 0);
+ return stdout.split("\0").filter((entry) => entry.length > 0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| const { stdout } = await runRipgrep(args, { | |
| cwd: rootPath, | |
| maxBuffer: FILE_LISTING_RIPGREP_BUFFER_BYTES, | |
| }); | |
| return stdout | |
| .split("\0") | |
| .map((entry) => entry.trim()) | |
| .filter((entry) => entry.length > 0); | |
| try { | |
| const { stdout } = await runRipgrep(args, { | |
| cwd: rootPath, | |
| maxBuffer: FILE_LISTING_RIPGREP_BUFFER_BYTES, | |
| }); | |
| return stdout.split("\0").filter((entry) => entry.length > 0); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/workspace-fs/src/search.ts` around lines 540 - 548, The current
processing of ripgrep output in the block using runRipgrep (with
FILE_LISTING_RIPGREP_BUFFER_BYTES and cwd: rootPath) improperly calls
entry.trim(), which strips legitimate spaces in filenames; change the return
path to simply split by "\0" and filter out empty entries (i.e., replace
.split("\0").map(entry => entry.trim()).filter(entry => entry.length > 0) with
.split("\0").filter(entry => entry.length > 0)) so NUL-delimited filenames
(including spaces) are preserved.
Summary
右サイドバー Files タブと Cmd+P の「重い」「欲しいファイルが出てこない」問題を、VSCode Quick Open (
anythingQuickAccess.ts+fuzzyScorer.ts) のロジックに寄せて根本対応。Issue: #359
なにが変わるか
dist/,build/,coverage/配下のファイルが検索でヒットしない.gitignore尊重 (VSCode と同等).gitignoreされたファイルを狙って探せないincludeHidden=trueで全部出るAbortControllerで中断主な変更点
packages/workspace-fs/src/search.tsbuildSearchIndexをripgrep --filesベースに切替、fast-glob フォールバック付き.gitignore/.rgignore/.git/info/exclude/ nested gitignore を自動尊重DEFAULT_IGNORE_PATTERNSを.git/+node_modules/のみに縮小FALLBACK_IGNORE_PATTERNSで fast-glob 時のみ dist/build/.next/.turbo/coverage を追加除外searchFilesにopenFilePaths/recentFilePaths/signal/runRipgrepパラメータを追加AbortControllerベースのキャンセル機構を追加 (workspace 単位)、スコアリングループに yield ポイントwarmupSearchIndexAPI をエクスポート_collectExactFileSearchMatches, compact index maps 等) を削除tRPC / service 層
core/service.ts,host/service.ts,client/index.tsに新パラメータ +warmupSearchIndexを追加UI (
apps/desktop)useFileSearch/useWorkspaceFileSearchにopenFilePaths/recentFilePaths/includePattern/excludePatternを追加 (feature parity)useCommandPaletteで MRU / open の絶対パスを受け取りsearchFilesに転送、ダイアログ初回オープン時に warmupv2-workspace/page.tsxから open/recent の絶対パスリストをuseCommandPaletteに渡すFileTreeToolbarの自前 debounce を削除 (hook 側useDebouncedValue(150)に一本化)テスト
searchFiles: open file boost / MRU tiebreaker / warmup の 3 ケース追加 (計 35 pass)設計メモ
DEFAULT_IGNORE_PATTERNSは patch event (shouldIndexRelativePath) でも使われるため最小限に。TTL 切れ時の rebuild で ripgrep による再フィルタがかかるので整合性は担保されるTest plan
.gitignoreに書かれたファイルがデフォルトで検索結果に出ないことを確認bun test/bun run typecheck/bun run lint全通過 (確認済)Summary by CodeRabbit
リリースノート
New Features
Performance Improvements
Bug Fixes