diff options
| author | Yuki Okita <[email protected]> | 2025-07-31 05:38:20 +0900 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-30 20:38:20 +0000 |
| commit | c1fe6889569610878c45216556fb99424b5bcba4 (patch) | |
| tree | b96f5f66bc00426fcd3e4b87402067342abbce12 /packages/cli/src/ui/hooks/useCompletion.ts | |
| parent | 21965f986c8aa99da5a0f8e52ae823bb2f040d7a (diff) | |
feat: Multi-Directory Workspace Support (part1: add `--include-directories` option) (#4605)
Co-authored-by: Allen Hutchison <[email protected]>
Diffstat (limited to 'packages/cli/src/ui/hooks/useCompletion.ts')
| -rw-r--r-- | packages/cli/src/ui/hooks/useCompletion.ts | 145 |
1 files changed, 81 insertions, 64 deletions
diff --git a/packages/cli/src/ui/hooks/useCompletion.ts b/packages/cli/src/ui/hooks/useCompletion.ts index 10724c21..4b106c1b 100644 --- a/packages/cli/src/ui/hooks/useCompletion.ts +++ b/packages/cli/src/ui/hooks/useCompletion.ts @@ -43,6 +43,7 @@ export interface UseCompletionReturn { export function useCompletion( buffer: TextBuffer, + dirs: readonly string[], cwd: string, slashCommands: readonly SlashCommand[], commandContext: CommandContext, @@ -328,8 +329,6 @@ export function useCompletion( : partialPath.substring(lastSlashIndex + 1), ); - const baseDirAbsolute = path.resolve(cwd, baseDirRelative); - let isMounted = true; const findFilesRecursively = async ( @@ -358,7 +357,7 @@ export function useCompletion( const entryPathRelative = path.join(currentRelativePath, entry.name); const entryPathFromRoot = path.relative( - cwd, + startDir, path.join(startDir, entry.name), ); @@ -417,29 +416,31 @@ export function useCompletion( respectGitIgnore?: boolean; respectGeminiIgnore?: boolean; }, + searchDir: string, maxResults = 50, ): Promise<Suggestion[]> => { const globPattern = `**/${searchPrefix}*`; const files = await glob(globPattern, { - cwd, + cwd: searchDir, dot: searchPrefix.startsWith('.'), nocase: true, }); const suggestions: Suggestion[] = files - .map((file: string) => ({ - label: file, - value: escapePath(file), - })) - .filter((s) => { + .filter((file) => { if (fileDiscoveryService) { - return !fileDiscoveryService.shouldIgnoreFile( - s.label, - filterOptions, - ); // relative path + return !fileDiscoveryService.shouldIgnoreFile(file, filterOptions); } return true; }) + .map((file: string) => { + const absolutePath = path.resolve(searchDir, file); + const label = path.relative(cwd, absolutePath); + return { + label, + value: escapePath(label), + }; + }) .slice(0, maxResults); return suggestions; @@ -456,63 +457,78 @@ export function useCompletion( config?.getFileFilteringOptions() ?? DEFAULT_FILE_FILTERING_OPTIONS; try { - // If there's no slash, or it's the root, do a recursive search from cwd - if ( - partialPath.indexOf('/') === -1 && - prefix && - enableRecursiveSearch - ) { - if (fileDiscoveryService) { - fetchedSuggestions = await findFilesWithGlob( - prefix, - fileDiscoveryService, - filterOptions, - ); + // If there's no slash, or it's the root, do a recursive search from workspace directories + for (const dir of dirs) { + let fetchedSuggestionsPerDir: Suggestion[] = []; + if ( + partialPath.indexOf('/') === -1 && + prefix && + enableRecursiveSearch + ) { + if (fileDiscoveryService) { + fetchedSuggestionsPerDir = await findFilesWithGlob( + prefix, + fileDiscoveryService, + filterOptions, + dir, + ); + } else { + fetchedSuggestionsPerDir = await findFilesRecursively( + dir, + prefix, + null, + filterOptions, + ); + } } else { - fetchedSuggestions = await findFilesRecursively( - cwd, - prefix, - null, - filterOptions, - ); - } - } else { - // Original behavior: list files in the specific directory - const lowerPrefix = prefix.toLowerCase(); - const entries = await fs.readdir(baseDirAbsolute, { - withFileTypes: true, - }); + // Original behavior: list files in the specific directory + const lowerPrefix = prefix.toLowerCase(); + const baseDirAbsolute = path.resolve(dir, baseDirRelative); + const entries = await fs.readdir(baseDirAbsolute, { + withFileTypes: true, + }); - // Filter entries using git-aware filtering - const filteredEntries = []; - for (const entry of entries) { - // Conditionally ignore dotfiles - if (!prefix.startsWith('.') && entry.name.startsWith('.')) { - continue; - } - if (!entry.name.toLowerCase().startsWith(lowerPrefix)) continue; + // Filter entries using git-aware filtering + const filteredEntries = []; + for (const entry of entries) { + // Conditionally ignore dotfiles + if (!prefix.startsWith('.') && entry.name.startsWith('.')) { + continue; + } + if (!entry.name.toLowerCase().startsWith(lowerPrefix)) continue; - const relativePath = path.relative( - cwd, - path.join(baseDirAbsolute, entry.name), - ); - if ( - fileDiscoveryService && - fileDiscoveryService.shouldIgnoreFile(relativePath, filterOptions) - ) { - continue; + const relativePath = path.relative( + dir, + path.join(baseDirAbsolute, entry.name), + ); + if ( + fileDiscoveryService && + fileDiscoveryService.shouldIgnoreFile( + relativePath, + filterOptions, + ) + ) { + continue; + } + + filteredEntries.push(entry); } - filteredEntries.push(entry); + fetchedSuggestionsPerDir = filteredEntries.map((entry) => { + const absolutePath = path.resolve(baseDirAbsolute, entry.name); + const label = + cwd === dir ? entry.name : path.relative(cwd, absolutePath); + const suggestionLabel = entry.isDirectory() ? label + '/' : label; + return { + label: suggestionLabel, + value: escapePath(suggestionLabel), + }; + }); } - - fetchedSuggestions = filteredEntries.map((entry) => { - const label = entry.isDirectory() ? entry.name + '/' : entry.name; - return { - label, - value: escapePath(label), // Value for completion should be just the name part - }; - }); + fetchedSuggestions = [ + ...fetchedSuggestions, + ...fetchedSuggestionsPerDir, + ]; } // Like glob, we always return forwardslashes, even in windows. @@ -585,6 +601,7 @@ export function useCompletion( }; }, [ buffer.text, + dirs, cwd, isActive, resetCompletionState, |
