提交 eb34efe9 编写于 作者: J Johannes Rieken

fuzzMatch3, more perf

上级 4afa2dfe
...@@ -383,3 +383,90 @@ export function matchesFuzzy2(pattern: string, word: string): IMatch[] | undefin ...@@ -383,3 +383,90 @@ export function matchesFuzzy2(pattern: string, word: string): IMatch[] | undefin
return result.length > 0 ? result : undefined; return result.length > 0 ? result : undefined;
} }
export function matchesFuzzy3(pattern: string, word: string) {
return _doMatchesFuzzy3(
pattern, pattern.toLowerCase(), 0,
word, word.toLowerCase(), 0,
[], 0
);
}
function _doMatchesFuzzy3(
pattern: string, lowPattern: string, patternPos: number,
word: string, lowWord: string, wordPos: number,
positions: number[], score: number
): [IMatch[], number] {
let retryPoints: number[] = [];
let lastDidMatch = false;
while (patternPos < lowPattern.length && wordPos < lowWord.length) {
const charLowPattern = lowPattern.charAt(patternPos);
const charLowWord = lowWord.charAt(wordPos);
if (charLowPattern === charLowWord) {
if (positions.length === 0) {
score = -Math.min(wordPos, 3) * 3; // penalty -> gaps at start
} else if (lastDidMatch) {
score += 1; // bonus -> subsequent match
}
if (charLowWord !== word.charAt(wordPos)) {
score += 10; // bonus -> upper-case
} else if (wordPos > 0 && word.charAt(wordPos).match(_separator)) {
score += 10; // bonus -> after a separator
} else {
// keep this as a retry point
retryPoints.push(patternPos, wordPos + 1, positions.length, score);
}
patternPos += 1;
positions.push(wordPos);
lastDidMatch = true;
} else {
lastDidMatch = false;
score -= 1; // penalty -> gaps in match
}
wordPos += 1;
}
if (positions.length === 0) {
return undefined;
}
const matches: IMatch[] = [];
let lastMatch: IMatch;
for (const pos of positions) {
if (lastMatch && lastMatch.end === pos) {
lastMatch.end += 1;
} else {
lastMatch = { start: pos, end: pos + 1 };
matches.push(lastMatch);
}
}
let result: [IMatch[], number] = [matches, score];
// try alternative matches
for (let i = 0; i < retryPoints.length; i += 4) {
const alt = _doMatchesFuzzy3(
pattern, lowPattern, retryPoints[i],
word, lowWord, retryPoints[i + 1],
positions.slice(0, retryPoints[i + 2]), retryPoints[i + 3]
);
if (alt && alt[1] > result[1]) {
result = alt;
}
}
return result;
}
const _separator = /[-_. ]/;
...@@ -5,10 +5,11 @@ ...@@ -5,10 +5,11 @@
'use strict'; 'use strict';
import * as assert from 'assert'; import * as assert from 'assert';
import { fuzzyContiguousFilter, matchesFuzzy2 } from 'vs/base/common/filters'; import { fuzzyContiguousFilter, matchesFuzzy2, matchesFuzzy3 } from 'vs/base/common/filters';
const fuzz = require.__$__nodeRequire('fuzzaldrin-plus');
const data = <{ label: string }[]>require.__$__nodeRequire(require.toUrl('./filters.perf.data.json')); const data = <{ label: string }[]>require.__$__nodeRequire(require.toUrl('./filters.perf.data.json'));
const patterns = ['cci', 'CCI', 'ida', 'pos', 'enbled', 'callback', 'gGame']; const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame'];
const _enablePerf = true; const _enablePerf = true;
...@@ -35,7 +36,7 @@ perfSuite('Performance - fuzzyMatch', function () { ...@@ -35,7 +36,7 @@ perfSuite('Performance - fuzzyMatch', function () {
} }
} }
} }
console.log(Date.now() - t1, count); console.log(Date.now() - t1, count, (count / (Date.now() - t1)).toPrecision(5));
assert.ok(count > 0); assert.ok(count > 0);
}); });
...@@ -52,7 +53,42 @@ perfSuite('Performance - fuzzyMatch', function () { ...@@ -52,7 +53,42 @@ perfSuite('Performance - fuzzyMatch', function () {
} }
} }
} }
console.log(Date.now() - t1, count); console.log(Date.now() - t1, count, (count / (Date.now() - t1)).toPrecision(5));
assert.ok(count > 0);
});
test('matchesFuzzy3', function () {
const t1 = Date.now();
let count = 0;
for (const pattern of patterns) {
for (const item of data) {
if (item.label) {
const matches = matchesFuzzy3(pattern, item.label);
if (matches) {
count += 1;
}
}
}
}
console.log(Date.now() - t1, count, (count / (Date.now() - t1)).toPrecision(5));
assert.ok(count > 0);
});
test('fuzzaldrin', function () {
const t1 = Date.now();
let count = 0;
for (const pattern of patterns) {
for (const item of data) {
if (item.label) {
const matches = fuzz.match(item.label, pattern);
// fuzz.score(item.label, pattern);
if (matches) {
count += 1;
}
}
}
}
console.log(Date.now() - t1, count, (count / (Date.now() - t1)).toPrecision(5));
assert.ok(count > 0); assert.ok(count > 0);
}); });
}); });
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
'use strict'; 'use strict';
import * as assert from 'assert'; import * as assert from 'assert';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords } from 'vs/base/common/filters'; import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, matchesFuzzy3 } from 'vs/base/common/filters';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) { function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst); let r = filter(word, wordToMatchAgainst);
...@@ -192,4 +192,36 @@ suite('Filters', () => { ...@@ -192,4 +192,36 @@ suite('Filters', () => {
assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null); assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null);
assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]);
}); });
test('FuzzyMatchAndScore', function () {
// function toString(word: string, matches: IMatch[], score: number) {
// let underline = '';
// let lastEnd = 0;
// for (const { start, end } of matches) {
// underline += new Array(1 + start - lastEnd).join(' ');
// underline += new Array(1 + end - start).join('^');
// lastEnd = end;
// }
// return `${word} (score: ${score})\n${underline}`;
// }
let [matches] = matchesFuzzy3('LLL', 'SVisualLoggerLogsList');
assert.deepEqual(matches, [{ start: 7, end: 8 }, { start: 13, end: 14 }, { start: 17, end: 18 }]);
[matches] = matchesFuzzy3('foobar', 'foobar');
assert.deepEqual(matches, [{ start: 0, end: 6 }]);
[matches] = matchesFuzzy3('tk', 'The Black Knight');
assert.deepEqual(matches, [{ start: 0, end: 1 }, { start: 10, end: 11 }]);
[matches] = matchesFuzzy3('Cat', 'charAt');
assert.deepEqual(matches, [{ start: 0, end: 1 }, { start: 4, end: 6 }]);
[matches] = matchesFuzzy3('Cat', 'charCodeAt');
assert.deepEqual(matches, [{ start: 4, end: 5 }, { start: 8, end: 10 }]);
[matches] = matchesFuzzy3('Cat', 'concat');
assert.deepEqual(matches, [{ start: 0, end: 1 }, { start: 4, end: 6 }]);
});
}); });
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册