From 6f431ce654a8270a25f0c28251a65e2c0861f06b Mon Sep 17 00:00:00 2001 From: Vincent Fugnitto Date: Thu, 4 Jul 2019 08:29:50 -0400 Subject: [PATCH] Update file-search to prioritize exact and better fuzzy matches Fixes #5636 - fixes an issue where better file results were not being displayed by the file-search due to the limit. The limit meant that if we ever reached the quota of results allowed some better results were never to be displayed. Instead, the logic was changed so that fuzzy matches are sorted by their score (how well they match the `searchPattern`), then are sent to the front end limited by the option. This means that all possible exact matches are sent and the remaining best fuzzy matches along with them until the limit is reached. Signed-off-by: Vincent Fugnitto --- .../src/node/file-search-service-impl.spec.ts | 10 ++++++ .../src/node/file-search-service-impl.ts | 32 ++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/file-search/src/node/file-search-service-impl.spec.ts b/packages/file-search/src/node/file-search-service-impl.spec.ts index 70cfb4bc86cda..b965038dffb82 100644 --- a/packages/file-search/src/node/file-search-service-impl.spec.ts +++ b/packages/file-search/src/node/file-search-service-impl.spec.ts @@ -46,6 +46,16 @@ describe('search-service', function () { service = testContainer.get(FileSearchServiceImpl); }); + it('should find and prioritize exact file matches', async () => { + const pattern = 'file-search-service-impl'; + const rootUri = FileUri.create(path.resolve(__dirname, '..')).toString(); + const matches = await service.find(pattern, { rootUris: [rootUri] }); + const expectedFile = FileUri.create(__filename).displayName; + const testFile = matches.find(e => e.endsWith(expectedFile)); + expect(matches[0]).contains(pattern); + expect(testFile).to.be.not.undefined; + }); + it('shall fuzzy search this spec file', async () => { const rootUri = FileUri.create(path.resolve(__dirname, '..')).toString(); const matches = await service.find('spc', { rootUris: [rootUri] }); diff --git a/packages/file-search/src/node/file-search-service-impl.ts b/packages/file-search/src/node/file-search-service-impl.ts index 84076019b780b..c0b2112a04c7f 100644 --- a/packages/file-search/src/node/file-search-service-impl.ts +++ b/packages/file-search/src/node/file-search-service-impl.ts @@ -85,9 +85,6 @@ export class FileSearchServiceImpl implements FileSearchService { } else if (opts.fuzzyMatch && fuzzy.test(searchPattern, candidate)) { fuzzyMatches.add(fileUri); } - if (exactMatches.size + fuzzyMatches.size === opts.limit) { - cancellationSource.cancel(); - } }, token); } catch (e) { console.error('Failed to search:', root, e); @@ -96,7 +93,10 @@ export class FileSearchServiceImpl implements FileSearchService { if (clientToken && clientToken.isCancellationRequested) { return []; } - return [...exactMatches, ...fuzzyMatches]; + // Sort the fuzzy matches based on their score. + const sortedFuzzyMatches = Array.from(fuzzyMatches).sort((a: string, b: string) => this.compareFuzzyMatches(a, b, searchPattern)); + // Return the limited list of exact and sorted matches. + return [...exactMatches, ...sortedFuzzyMatches].slice(0, opts.limit); } private doFind(rootUri: URI, options: FileSearchService.BaseOptions, @@ -150,4 +150,28 @@ export class FileSearchServiceImpl implements FileSearchService { return args; } + /** + * Compare two fuzzy matches based on their score. + * + * @param a {string} comparison string. + * @param b {string} comparison string. + * @param searchPattern the search pattern to look for. + */ + private compareFuzzyMatches(a: string, b: string, searchPattern: string): number { + const scoreA: number = this.score(a, searchPattern); + const scoreB: number = this.score(b, searchPattern); + return scoreB - scoreA; + } + + /** + * Score a given file uri against the `searchPattern`. + * @param uri {string} the file uri. + * @param searchPattern {string} the search pattern to look for. + * + * @returns the fuzzy match score. + */ + private score(uri: string, searchPattern: string): number { + const match = fuzzy.match(searchPattern, uri); + return (match === null) ? 0 : match.score; + } }