* Get file list for a directory, using cache if available
(
dir: string,
matchDirs: boolean
)
| 399 | * Get file list for a directory, using cache if available |
| 400 | */ |
| 401 | async function getFileList( |
| 402 | dir: string, |
| 403 | matchDirs: boolean |
| 404 | ): Promise<{ items: string[]; source: "git" | "ripgrep" | "fs"; tree: TreeNode }> { |
| 405 | const cacheKey = `${dir}:${matchDirs}` |
| 406 | |
| 407 | // Check cache |
| 408 | const cached = fileListCache.get(cacheKey) |
| 409 | if (cached) { |
| 410 | logger.info("[Files:getFileList] Using cached file list", JSON.stringify({ |
| 411 | dir, |
| 412 | matchDirs, |
| 413 | count: cached.items.length, |
| 414 | })) |
| 415 | return { items: cached.items, source: cached.source, tree: cached.tree } |
| 416 | } |
| 417 | |
| 418 | let items: string[] = [] |
| 419 | let source: "git" | "ripgrep" | "fs" = "fs" |
| 420 | |
| 421 | // Strategy 1: Use ripgrep if available (faster than git ls-files) |
| 422 | const rgPath = (await getRipgrepPath()) || getManagedRipgrepPath() |
| 423 | if (rgPath) { |
| 424 | items = await listFilesWithRipgrep({ rgPath, dir, matchDirs }) |
| 425 | source = "ripgrep" |
| 426 | logger.info("[Files:getFileList] Using ripgrep", JSON.stringify({ path: rgPath, count: items.length })) |
| 427 | } |
| 428 | |
| 429 | // Strategy 2: Use git if ripgrep didn't work |
| 430 | if (items.length === 0 && (await isGitRepo(dir))) { |
| 431 | items = await listFilesWithGit({ dir, matchDirs }) |
| 432 | source = "git" |
| 433 | logger.info("[Files:getFileList] Using git ls-files", JSON.stringify({ count: items.length })) |
| 434 | } |
| 435 | |
| 436 | // Strategy 3: Fallback to filesystem walk |
| 437 | if (items.length === 0) { |
| 438 | items = listFilesWithFs({ dir, matchDirs }) |
| 439 | source = "fs" |
| 440 | logger.info("[Files:getFileList] Using filesystem walk", JSON.stringify({ count: items.length })) |
| 441 | } |
| 442 | |
| 443 | // Build tree for directory browsing |
| 444 | const tree = buildTree(items) |
| 445 | |
| 446 | // Cache with auto-expiry |
| 447 | const timeoutId = setTimeout(() => { |
| 448 | fileListCache.delete(cacheKey) |
| 449 | logger.info("[Files:getFileList] Cache expired", JSON.stringify({ cacheKey })) |
| 450 | }, FILE_LIST_CACHE_TTL_MS) |
| 451 | |
| 452 | fileListCache.set(cacheKey, { items, source, tree, timeoutId }) |
| 453 | |
| 454 | return { items, source, tree } |
| 455 | } |
| 456 | |
| 457 | /** |
| 458 | * Fuzzy search for files or directories |
no test coverage detected