feat: Improve @-command file path completion with fzf integration (#5650)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
4782113ceb
commit
9ac3e8b79e
|
@ -5540,6 +5540,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fzf": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/gcp-metadata": {
|
"node_modules/gcp-metadata": {
|
||||||
"version": "6.1.1",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
|
||||||
|
@ -11889,6 +11895,7 @@
|
||||||
"diff": "^7.0.0",
|
"diff": "^7.0.0",
|
||||||
"dotenv": "^17.1.0",
|
"dotenv": "^17.1.0",
|
||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
|
"fzf": "^0.5.2",
|
||||||
"glob": "^10.4.5",
|
"glob": "^10.4.5",
|
||||||
"google-auth-library": "^9.11.0",
|
"google-auth-library": "^9.11.0",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
|
|
|
@ -114,8 +114,8 @@ describe('useAtCompletion', () => {
|
||||||
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
||||||
'src/',
|
'src/',
|
||||||
'src/components/',
|
'src/components/',
|
||||||
'src/components/Button.tsx',
|
|
||||||
'src/index.js',
|
'src/index.js',
|
||||||
|
'src/components/Button.tsx',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
"diff": "^7.0.0",
|
"diff": "^7.0.0",
|
||||||
"dotenv": "^17.1.0",
|
"dotenv": "^17.1.0",
|
||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
|
"fzf": "^0.5.2",
|
||||||
"glob": "^10.4.5",
|
"glob": "^10.4.5",
|
||||||
"google-auth-library": "^9.11.0",
|
"google-auth-library": "^9.11.0",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
|
|
|
@ -290,6 +290,30 @@ describe('FileSearch', () => {
|
||||||
expect(results).toEqual(['src/file1.js', 'src/file2.js']); // Assuming alphabetical sort
|
expect(results).toEqual(['src/file1.js', 'src/file2.js']); // Assuming alphabetical sort
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use fzf for fuzzy matching when pattern does not contain wildcards', async () => {
|
||||||
|
tmpDir = await createTmpDir({
|
||||||
|
src: {
|
||||||
|
'main.js': '',
|
||||||
|
'util.ts': '',
|
||||||
|
'style.css': '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileSearch = new FileSearch({
|
||||||
|
projectRoot: tmpDir,
|
||||||
|
useGitignore: false,
|
||||||
|
useGeminiignore: false,
|
||||||
|
ignoreDirs: [],
|
||||||
|
cache: false,
|
||||||
|
cacheTtl: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
await fileSearch.initialize();
|
||||||
|
const results = await fileSearch.search('sst');
|
||||||
|
|
||||||
|
expect(results).toEqual(['src/style.css']);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return empty array when no matches are found', async () => {
|
it('should return empty array when no matches are found', async () => {
|
||||||
tmpDir = await createTmpDir({
|
tmpDir = await createTmpDir({
|
||||||
src: ['file1.js'],
|
src: ['file1.js'],
|
||||||
|
|
|
@ -11,6 +11,7 @@ import picomatch from 'picomatch';
|
||||||
import { Ignore } from './ignore.js';
|
import { Ignore } from './ignore.js';
|
||||||
import { ResultCache } from './result-cache.js';
|
import { ResultCache } from './result-cache.js';
|
||||||
import * as cache from './crawlCache.js';
|
import * as cache from './crawlCache.js';
|
||||||
|
import { Fzf, FzfResultItem } from 'fzf';
|
||||||
|
|
||||||
export type FileSearchOptions = {
|
export type FileSearchOptions = {
|
||||||
projectRoot: string;
|
projectRoot: string;
|
||||||
|
@ -77,6 +78,18 @@ export async function filter(
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters a list of paths based on a given pattern using fzf.
|
||||||
|
* @param allPaths The list of all paths to filter.
|
||||||
|
* @param pattern The fzf pattern to filter by.
|
||||||
|
* @returns The filtered and sorted list of paths.
|
||||||
|
*/
|
||||||
|
function filterByFzf(allPaths: string[], pattern: string) {
|
||||||
|
return new Fzf(allPaths)
|
||||||
|
.find(pattern)
|
||||||
|
.map((entry: FzfResultItem) => entry.item);
|
||||||
|
}
|
||||||
|
|
||||||
export type SearchOptions = {
|
export type SearchOptions = {
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
maxResults?: number;
|
maxResults?: number;
|
||||||
|
@ -137,7 +150,9 @@ export class FileSearch {
|
||||||
filteredCandidates = candidates;
|
filteredCandidates = candidates;
|
||||||
} else {
|
} else {
|
||||||
// Apply the user's picomatch pattern filter
|
// Apply the user's picomatch pattern filter
|
||||||
filteredCandidates = await filter(candidates, pattern, options.signal);
|
filteredCandidates = pattern.includes('*')
|
||||||
|
? await filter(candidates, pattern, options.signal)
|
||||||
|
: filterByFzf(this.allFiles, pattern);
|
||||||
this.resultCache!.set(pattern, filteredCandidates);
|
this.resultCache!.set(pattern, filteredCandidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue