Skip to content

Commit

Permalink
Add Whitespace Query Support to Quick File Open
Browse files Browse the repository at this point in the history
What it Does

Fixes [#8747](#8747)

- Allows whitespaces to be included in a `quick file open` search query.
- Performs both an exact and fuzzy search on the query with whitespaces.
- Adds tests for whitespace queries (considers fuzzy matching and search
  term order).

How to Test

1. `ctrl + p` to `quick file open`
2. Search for a file with a query that includes whitespaces (eg. `readme core`)
3. Observe that whitespaces do not affect the search results

Alternatively, run `@theia/file-search` tests.

Signed-off-by: seantan22 <sean.a.tan@ericsson.com>
  • Loading branch information
seantan22 committed Jan 26, 2021
1 parent 47be972 commit 5cba075
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 3 deletions.
30 changes: 30 additions & 0 deletions packages/file-search/src/node/file-search-service-impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,34 @@ describe('search-service', function (): void {
});
});

describe('search with whitespaces', () => {
const rootUri = FileUri.create(path.resolve(__dirname, '../../test-resources')).toString();

it('should support file searches with whitespaces', async () => {
const matches = await service.find('foo sub', { rootUris: [rootUri], fuzzyMatch: true, useGitIgnore: true, limit: 200 });

expect(matches).to.be.length(2);
expect(matches[0].endsWith('subdir1/sub-bar/foo.txt'));
expect(matches[1].endsWith('subdir1/sub2/foo.txt'));
});

it('should support fuzzy file searches with whitespaces', async () => {
const matchesExact = await service.find('foo sbd2', { rootUris: [rootUri], fuzzyMatch: false, useGitIgnore: true, limit: 200 });
const matchesFuzzy = await service.find('foo sbd2', { rootUris: [rootUri], fuzzyMatch: true, useGitIgnore: true, limit: 200 });

expect(matchesExact).to.be.length(0);
expect(matchesFuzzy).to.be.length(1);
expect(matchesFuzzy[0].endsWith('subdir1/sub2/foo.txt'));
});

it('should support file searches with whitespaces regardless of order', async () => {
const matchesA = await service.find('foo sub', { rootUris: [rootUri], fuzzyMatch: true, useGitIgnore: true, limit: 200 });
const matchesB = await service.find('sub foo', { rootUris: [rootUri], fuzzyMatch: true, useGitIgnore: true, limit: 200 });

expect(matchesA).to.not.be.empty;
expect(matchesB).to.not.be.empty;
expect(matchesA).to.deep.eq(matchesB);
});
});

});
21 changes: 18 additions & 3 deletions packages/file-search/src/node/file-search-service-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,39 @@ export class FileSearchServiceImpl implements FileSearchService {
searchPattern = searchPattern.replace(/\//g, '\\');
}

const WHITESPACE_QUERY_SEPARATOR = ' ';
const stringPattern = searchPattern.toLocaleLowerCase();
const stringPatterns = stringPattern.split(WHITESPACE_QUERY_SEPARATOR);

await Promise.all(Object.keys(roots).map(async root => {
try {
const rootUri = new URI(root);
const rootPath = FileUri.fsPath(rootUri);
const rootOptions = roots[root];

await this.doFind(rootUri, rootOptions, candidate => {

// Convert OS-native candidate path to a file URI string
const fileUri = FileUri.create(path.resolve(rootPath, candidate)).toString();

// Skip results that have already been matched.
if (exactMatches.has(fileUri) || fuzzyMatches.has(fileUri)) {
return;
}
if (!searchPattern || searchPattern === '*' || candidate.toLocaleLowerCase().indexOf(stringPattern) !== -1) {

// Determine if the candidate matches any of the patterns exactly or fuzzy
const patternExists = stringPatterns.every(pattern => candidate.toLocaleLowerCase().indexOf(pattern) !== -1);
if (patternExists) {
exactMatches.add(fileUri);
} else if (!searchPattern || searchPattern === '*') {
exactMatches.add(fileUri);
} else if (opts.fuzzyMatch && fuzzy.test(searchPattern, candidate)) {
fuzzyMatches.add(fileUri);
} else {
const fuzzyPatternExists = stringPatterns.every(pattern => fuzzy.test(pattern, candidate));
if (opts.fuzzyMatch && fuzzyPatternExists) {
fuzzyMatches.add(fileUri);
}
}

// Preemptively terminate the search when the list of exact matches reaches the limit.
if (exactMatches.size === opts.limit) {
cancellationSource.cancel();
Expand Down

0 comments on commit 5cba075

Please sign in to comment.