( partialPath: string, showOnEmpty = false, )
| 713 | * @param showOnEmpty Whether to show suggestions even if partialPath is empty (used for @ symbol) |
| 714 | */ |
| 715 | export async function generateFileSuggestions( |
| 716 | partialPath: string, |
| 717 | showOnEmpty = false, |
| 718 | ): Promise<SuggestionItem[]> { |
| 719 | // If input is empty and we don't want to show suggestions on empty, return nothing |
| 720 | if (!partialPath && !showOnEmpty) { |
| 721 | return [] |
| 722 | } |
| 723 | |
| 724 | // Use custom command directly if configured. We don't mix in our config files |
| 725 | // because the command returns pre-ranked results using its own search logic. |
| 726 | if (getInitialSettings().fileSuggestion?.type === 'command') { |
| 727 | const input: FileSuggestionCommandInput = { |
| 728 | ...createBaseHookInput(), |
| 729 | query: partialPath, |
| 730 | } |
| 731 | const results = await executeFileSuggestionCommand(input) |
| 732 | return results.slice(0, MAX_SUGGESTIONS).map(createFileSuggestionItem) |
| 733 | } |
| 734 | |
| 735 | // If the partial path is empty or just a dot, return current directory suggestions |
| 736 | if (partialPath === '' || partialPath === '.' || partialPath === './') { |
| 737 | const topLevelPaths = await getTopLevelPaths() |
| 738 | startBackgroundCacheRefresh() |
| 739 | return topLevelPaths.slice(0, MAX_SUGGESTIONS).map(createFileSuggestionItem) |
| 740 | } |
| 741 | |
| 742 | const startTime = Date.now() |
| 743 | |
| 744 | try { |
| 745 | // Kick a background refresh. The index is progressively queryable — |
| 746 | // searches during build return partial results from ready chunks, and |
| 747 | // the typeahead callback (setOnIndexBuildComplete) re-fires the search |
| 748 | // when the build finishes to upgrade partial → full. |
| 749 | const wasBuilding = fileListRefreshPromise !== null |
| 750 | startBackgroundCacheRefresh() |
| 751 | |
| 752 | // Handle both './' and '.\' |
| 753 | let normalizedPath = partialPath |
| 754 | const currentDirPrefix = '.' + path.sep |
| 755 | if (partialPath.startsWith(currentDirPrefix)) { |
| 756 | normalizedPath = partialPath.substring(2) |
| 757 | } |
| 758 | |
| 759 | // Handle tilde expansion for home directory |
| 760 | if (normalizedPath.startsWith('~')) { |
| 761 | normalizedPath = expandPath(normalizedPath) |
| 762 | } |
| 763 | |
| 764 | const matches = fileIndex |
| 765 | ? findMatchingFiles(fileIndex, normalizedPath) |
| 766 | : [] |
| 767 | |
| 768 | const duration = Date.now() - startTime |
| 769 | logForDebugging( |
| 770 | `[FileIndex] generateFileSuggestions: ${matches.length} results in ${duration}ms (${wasBuilding ? 'partial' : 'full'} index)`, |
| 771 | ) |
| 772 | logEvent('tengu_file_suggestions_query', { |
no test coverage detected