feat(search): Add option to disable fuzzy search (#6510)

Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Arya Gummadi <aryagummadi@google.com>
This commit is contained in:
Bryant Chandler
2025-08-21 23:31:39 -07:00
committed by GitHub
parent ef46d64ae5
commit 4ced997d63
7 changed files with 114 additions and 2 deletions

View File

@@ -177,6 +177,7 @@ export interface ConfigParameters {
respectGitIgnore?: boolean;
respectGeminiIgnore?: boolean;
enableRecursiveFileSearch?: boolean;
disableFuzzySearch?: boolean;
};
checkpointing?: boolean;
proxy?: string;
@@ -237,6 +238,7 @@ export class Config {
respectGitIgnore: boolean;
respectGeminiIgnore: boolean;
enableRecursiveFileSearch: boolean;
disableFuzzySearch: boolean;
};
private fileDiscoveryService: FileDiscoveryService | null = null;
private gitService: GitService | undefined = undefined;
@@ -316,6 +318,7 @@ export class Config {
respectGeminiIgnore: params.fileFiltering?.respectGeminiIgnore ?? true,
enableRecursiveFileSearch:
params.fileFiltering?.enableRecursiveFileSearch ?? true,
disableFuzzySearch: params.fileFiltering?.disableFuzzySearch ?? false,
};
this.checkpointing = params.checkpointing ?? false;
this.proxy = params.proxy;
@@ -600,6 +603,10 @@ export class Config {
return this.fileFiltering.enableRecursiveFileSearch;
}
getFileFilteringDisableFuzzySearch(): boolean {
return this.fileFiltering.disableFuzzySearch;
}
getFileFilteringRespectGitIgnore(): boolean {
return this.fileFiltering.respectGitIgnore;
}

View File

@@ -32,6 +32,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -57,6 +58,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -84,6 +86,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -112,6 +115,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -144,6 +148,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -167,6 +172,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -201,6 +207,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -230,6 +237,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -259,6 +267,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
// Expect no errors to be thrown during initialization
@@ -285,6 +294,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -310,6 +320,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -318,6 +329,60 @@ describe('FileSearch', () => {
expect(results).toEqual(['src/style.css']);
});
it('should not use fzf for fuzzy matching when disableFuzzySearch is true', async () => {
tmpDir = await createTmpDir({
src: {
'file1.js': '',
'flexible.js': '',
'other.ts': '',
},
});
const fileSearch = FileSearchFactory.create({
projectRoot: tmpDir,
useGitignore: false,
useGeminiignore: false,
ignoreDirs: [],
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: true,
});
await fileSearch.initialize();
const results = await fileSearch.search('fle');
expect(results).toEqual(['src/flexible.js']);
});
it('should use fzf for fuzzy matching when disableFuzzySearch is false', async () => {
tmpDir = await createTmpDir({
src: {
'file1.js': '',
'flexible.js': '',
'other.ts': '',
},
});
const fileSearch = FileSearchFactory.create({
projectRoot: tmpDir,
useGitignore: false,
useGeminiignore: false,
ignoreDirs: [],
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
const results = await fileSearch.search('fle');
expect(results).toEqual(
expect.arrayContaining(['src/file1.js', 'src/flexible.js']),
);
});
it('should return empty array when no matches are found', async () => {
tmpDir = await createTmpDir({
src: ['file1.js'],
@@ -331,6 +396,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -361,6 +427,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await expect(fileSearch.search('')).rejects.toThrow(
@@ -382,6 +449,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -404,6 +472,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -427,6 +496,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -463,6 +533,7 @@ describe('FileSearch', () => {
cache: true, // Enable caching for this test
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -502,6 +573,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -545,6 +617,7 @@ describe('FileSearch', () => {
cache: true, // Ensure caching is enabled
cacheTtl: 10000,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -582,6 +655,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -611,6 +685,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: false,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -635,6 +710,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: false,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -659,6 +735,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: false,
disableFuzzySearch: false,
});
await fileSearch.initialize();
@@ -681,6 +758,7 @@ describe('FileSearch', () => {
cache: false,
cacheTtl: 0,
enableRecursiveFileSearch: false,
disableFuzzySearch: false,
});
await fileSearch.initialize();

View File

@@ -20,6 +20,7 @@ export interface FileSearchOptions {
cache: boolean;
cacheTtl: number;
enableRecursiveFileSearch: boolean;
disableFuzzySearch: boolean;
maxDepth?: number;
}
@@ -128,7 +129,7 @@ class RecursiveFileSearch implements FileSearch {
filteredCandidates = candidates;
} else {
let shouldCache = true;
if (pattern.includes('*')) {
if (pattern.includes('*') || this.options.disableFuzzySearch) {
filteredCandidates = await filter(candidates, pattern, options.signal);
} else {
filteredCandidates = await this.fzf