mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
better matchCompactness computation
This commit is contained in:
parent
e7799d1915
commit
be10b09e63
|
@ -16,17 +16,6 @@ export type ScorerCache = { [key: string]: IItemScore };
|
|||
|
||||
const NO_SCORE: Score = [0, []];
|
||||
|
||||
/**
|
||||
* Compute a score for the given string and the given query.
|
||||
*
|
||||
* Rules:
|
||||
* Character score: 1
|
||||
* Same case bonus: 1
|
||||
* Upper case bonus: 1
|
||||
* Consecutive match bonus: 5
|
||||
* Start of word/path bonus: 7
|
||||
* Start of string bonus: 8
|
||||
*/
|
||||
export function _doScore(target: string, query: string, fuzzy: boolean): Score {
|
||||
if (!target || !query) {
|
||||
return NO_SCORE; // return early if target or query are undefined
|
||||
|
@ -122,7 +111,7 @@ BEGIN THIRD PARTY
|
|||
* Date: Tue Mar 1 2011
|
||||
* Updated: Tue Mar 10 2015
|
||||
*/
|
||||
export function _doScoreFromOffset(target: string, query: string, targetLower: string, queryLower: string, queryLen: number, offset: number): Score {
|
||||
function _doScoreFromOffset(target: string, query: string, targetLower: string, queryLower: string, queryLen: number, offset: number): Score {
|
||||
const matchingPositions: number[] = [];
|
||||
|
||||
let targetIndex = offset;
|
||||
|
@ -357,14 +346,14 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy:
|
|||
const scoreA = itemScoreA.score;
|
||||
const scoreB = itemScoreB.score;
|
||||
|
||||
// 1.) check for identity matches
|
||||
// 1.) prefer identity matches
|
||||
if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === PATH_IDENTITY_SCORE ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 2.) check for label prefix matches
|
||||
// 2.) prefer label prefix matches
|
||||
if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_PREFIX_SCORE ? -1 : 1;
|
||||
|
@ -379,7 +368,7 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy:
|
|||
}
|
||||
}
|
||||
|
||||
// 3.) check for camelcase matches
|
||||
// 3.) prefer camelcase matches
|
||||
if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1;
|
||||
|
@ -400,7 +389,7 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy:
|
|||
}
|
||||
}
|
||||
|
||||
// 4.) check for label scores
|
||||
// 4.) prefer label scores
|
||||
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
|
||||
if (scoreB < LABEL_SCORE_THRESHOLD) {
|
||||
return -1;
|
||||
|
@ -411,20 +400,27 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy:
|
|||
}
|
||||
}
|
||||
|
||||
// 5.) check for path scores
|
||||
// 5.) compare by score
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA > scoreB ? -1 : 1;
|
||||
}
|
||||
|
||||
// 6.) scores are identical, prefer more compact matches (label and description)
|
||||
const labelMatchCompactness = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
|
||||
if (labelMatchCompactness !== 0) {
|
||||
return labelMatchCompactness;
|
||||
let itemAMatches: IMatch[] = [];
|
||||
if (itemScoreA.descriptionMatch) {
|
||||
itemAMatches.push(...itemScoreA.descriptionMatch);
|
||||
}
|
||||
itemAMatches.push(...itemScoreA.labelMatch);
|
||||
|
||||
const descriptionMatchCompactness = compareByMatchLength(itemScoreA.descriptionMatch, itemScoreB.descriptionMatch);
|
||||
if (descriptionMatchCompactness !== 0) {
|
||||
return descriptionMatchCompactness;
|
||||
let itemBMatches: IMatch[] = [];
|
||||
if (itemScoreB.descriptionMatch) {
|
||||
itemBMatches.push(...itemScoreB.descriptionMatch);
|
||||
}
|
||||
itemBMatches.push(...itemScoreB.labelMatch);
|
||||
|
||||
const matchCompactness = compareByMatchLength(itemAMatches, itemBMatches);
|
||||
if (matchCompactness !== 0) {
|
||||
return matchCompactness;
|
||||
}
|
||||
|
||||
// 7.) at this point, scores are identical and match compactness as well
|
||||
|
|
|
@ -166,6 +166,15 @@ suite('Quick Open Scorer', () => {
|
|||
assert.ok(pathRes.score > noRes.score);
|
||||
});
|
||||
|
||||
test('scoreItem - invalid input', function () {
|
||||
|
||||
let res = scorer.scoreItem(null, null, true, ResourceAccessor, cache);
|
||||
assert.equal(res.score, 0);
|
||||
|
||||
res = scorer.scoreItem(null, 'null', true, ResourceAccessor, cache);
|
||||
assert.equal(res.score, 0);
|
||||
});
|
||||
|
||||
test('scoreItem - optimize for file paths', function () {
|
||||
const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt');
|
||||
|
||||
|
@ -379,6 +388,25 @@ suite('Quick Open Scorer', () => {
|
|||
assert.equal(res[2], resourceC);
|
||||
});
|
||||
|
||||
test('compareFilesByScore - prefer shorter basenames (match on basename)', function () {
|
||||
const resourceA = URI.file('/some/path/fileA.txt');
|
||||
const resourceB = URI.file('/some/path/other/fileBLonger.txt');
|
||||
const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');
|
||||
|
||||
// Resource A part of path
|
||||
let query = 'file';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceC);
|
||||
assert.equal(res[2], resourceB);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceC);
|
||||
assert.equal(res[2], resourceB);
|
||||
});
|
||||
|
||||
test('compareFilesByScore - prefer shorter paths', function () {
|
||||
const resourceA = URI.file('/some/path/fileA.txt');
|
||||
const resourceB = URI.file('/some/path/other/fileB.txt');
|
||||
|
@ -471,6 +499,21 @@ suite('Quick Open Scorer', () => {
|
|||
assert.equal(res[1], resourceA);
|
||||
});
|
||||
|
||||
test('compareFilesByScore - prefer more compact matches (label and path)', function () {
|
||||
const resourceA = URI.file('config/example/thisfile.ts');
|
||||
const resourceB = URI.file('config/24234243244/example/file.js');
|
||||
|
||||
let query = 'exfile';
|
||||
|
||||
let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
|
||||
res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
});
|
||||
|
||||
test('compareFilesByScore - avoid match scattering (bug #34210)', function () {
|
||||
const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js');
|
||||
const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js');
|
||||
|
@ -539,4 +582,9 @@ suite('Quick Open Scorer', () => {
|
|||
let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
});
|
||||
|
||||
test('massageSearchForScoring', function () {
|
||||
assert.equal(scorer.massageSearchForScoring(' f*a '), 'fa');
|
||||
assert.equal(scorer.massageSearchForScoring('model tester.ts'), 'modeltester.ts');
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue