Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fuzzy matching on the absolute paths of files/resources #150450

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions src/vs/base/common/fuzzyScorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,8 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined,
}
}

// Finally compute description + label scores if we have a description
let itemScore = NO_ITEM_SCORE;
// Otherwise, compute description + label scores if we have a description
if (description) {
let descriptionPrefix = description;
if (!!path) {
Expand Down Expand Up @@ -540,11 +541,51 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined,
}
});

return { score: labelDescriptionScore, labelMatch, descriptionMatch };
itemScore = { score: labelDescriptionScore, labelMatch, descriptionMatch };
}
}

return NO_ITEM_SCORE;
// Finally try to compute a score based on the path if we have one.
// This allows us to partially match on the absolute path of the item
// instead of only matching on the workspace folder relative path.
//
// We assume the label is the path's basename and the description, if
// present, is a subpath of the path's dirname or '.'.
if (path?.endsWith(label) &&
(!description || description === '.' || path.endsWith(`${description}${sep}${label}`))) {
const [pathScore, pathPositions] = scoreFuzzy(
path,
query.normalized,
query.normalizedLowercase,
allowNonContiguousMatches && !query.expectContiguousMatch);

// FIXME: Should we ignore low quality matches across the paths? How?
if (pathScore > itemScore.score) {
const labelStartInPath = path.length - label.length;
let descEndInPath = -1;
let descStartInPath = -1;
if (description && description !== '.') {
descEndInPath = labelStartInPath - sep.length;
descStartInPath = descEndInPath - description.length;
}
const pathMatches = createMatches(pathPositions);
const labelMatch: IMatch[] = [];
const descriptionMatch: IMatch[] = [];

for (const match of pathMatches) {
if (match.end > labelStartInPath) {
labelMatch.push({ start: Math.max(0, match.start - labelStartInPath), end: match.end - labelStartInPath });
}
if (match.start < descEndInPath && descStartInPath < match.end) {
descriptionMatch.push({ start: Math.max(0, match.start - descStartInPath), end: Math.min(descEndInPath, match.end) - descStartInPath });
}
}

itemScore = { score: pathScore, labelMatch, descriptionMatch };
}
}

return itemScore;
}

function createMatches(offsets: number[] | undefined): IMatch[] {
Expand Down
9 changes: 8 additions & 1 deletion src/vs/workbench/services/search/common/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,14 @@ export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg i

export function isFilePatternMatch(candidate: IRawFileMatch, normalizedFilePatternLowercase: string): boolean {
const pathToMatch = candidate.searchPath ? candidate.searchPath : candidate.relativePath;
return fuzzyContains(pathToMatch, normalizedFilePatternLowercase);
if (fuzzyContains(pathToMatch, normalizedFilePatternLowercase)) {
return true;
}
// Also try matching against the absolute path of the file.
if (candidate.base) {
return fuzzyContains(paths.join(candidate.base, candidate.relativePath), normalizedFilePatternLowercase);
}
return false;
}

export interface ISerializedFileMatch {
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/services/search/node/rawSearchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ const FileMatchItemAccessor = new class implements IItemAccessor<IRawFileMatch>
}

getItemPath(match: IRawFileMatch): string {
if (match.base) {
return join(match.base, match.relativePath); // e.g. /home/user/some/path/to/file/myFile.txt
}
return match.relativePath; // e.g. some/path/to/file/myFile.txt
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,38 @@ flakySuite('RawSearchService', () => {
assert.deepStrictEqual(results, [path.normalize('/some/where/bbc'), path.normalize('/some/where/bab')]);
});

test('Sorted results match on absolute path', async function () {
const paths = ['foo/bab', 'foo/bbc', 'foo/abb'];
const matches: IRawFileMatch[] = paths.map(relativePath => ({
base: path.normalize('/some/where'),
relativePath,
basename: relativePath,
size: 3,
searchPath: undefined
}));
const Engine = TestSearchEngine.bind(null, () => matches.shift()!);
const service = new RawSearchService();

const results: any[] = [];
const cb: IProgressCallback = value => {
if (Array.isArray(value)) {
results.push(...value.map(v => v.path));
} else {
assert.fail(JSON.stringify(value));
}
};

await service.doFileSearchWithEngine(Engine, {
type: QueryType.File,
folderQueries: TEST_FOLDER_QUERIES,
filePattern: '/some/where/bb',
sortByScore: true,
maxResults: 2
}, cb, undefined, 1);
assert.notStrictEqual(typeof TestSearchEngine.last.config!.maxResults, 'number');
assert.deepStrictEqual(results, [path.normalize('/some/where/foo/bbc'), path.normalize('/some/where/foo/abb')]);
});

test('Sorted result batches', async function () {
let i = 25;
const Engine = TestSearchEngine.bind(null, () => i-- ? rawMatch : null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ flakySuite('FileSearchEngine', () => {
}
}, () => { }, (error) => {
assert.ok(!error);
assert.strictEqual(count, 7);
assert.strictEqual(count, 11);
done();
});
});
Expand All @@ -224,7 +224,7 @@ flakySuite('FileSearchEngine', () => {
}
}, () => { }, (error) => {
assert.ok(!error);
assert.strictEqual(count, 3);
assert.strictEqual(count, 7);
done();
});
});
Expand Down