better matchCompactness computation

This commit is contained in:
Benjamin Pasero 2017-10-10 17:47:17 +02:00
parent e7799d1915
commit be10b09e63
2 changed files with 67 additions and 23 deletions

View file

@ -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

View file

@ -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');
});
});