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

don't use charAt, make lcs-match almost work

上级 d43e80c8
......@@ -367,7 +367,7 @@ export function matchesFuzzy2(pattern: string, word: string): number[] {
let patternPos = 0;
let wordPos = 0;
while (patternPos < pattern.length && wordPos < word.length) {
if (pattern.charAt(patternPos) === word.charAt(wordPos)) {
if (pattern[patternPos] === word[wordPos]) {
patternPos += 1;
matches.push(wordPos);
}
......@@ -381,53 +381,97 @@ export function matchesFuzzy2(pattern: string, word: string): number[] {
return matches;
}
const table: number[][] = [];
const gap = -1;
let table: number[][];
(function initTable() {
table = [];
let zeroArray: number[] = [];
for (let i = 0; i < 100 + 1; ++i) {
zeroArray.push(-i);
let firstRow: number[] = [0];
table.push(firstRow);
for (let i = 1; i < 100; i++) {
firstRow.push(i * gap);
}
for (let i = 0; i < 100 + 1; ++i) {
let t = zeroArray.slice(0);
t[0] = -i;
table.push(t);
for (let i = 0; i < 100; i++) {
let row = new Array(100);
row[0] = i * gap;
table.push(row);
}
})();
function score(pattern: string, patternPos: number, word: string, wordPos: number) {
if (pattern.charAt(patternPos) === word.charAt(wordPos)) {
return 2;
} else {
return -2;
}
}
export function fuzzyLCS(pattern: string, word: string) {
export function fuzzyLCS(pattern: string, word: string): [number, number[]] {
const lowPattern = pattern.toLowerCase();
const upPattern = pattern.toUpperCase();
const lowWord = word.toLowerCase();
let matches: number[] = [];
let score = 0;
for (let i = 1; i < pattern.length + 1; ++i) {
let oneMatch = false;
let topMatch = -1;
let topMatchIdx = 0;
for (let j = 1; j < word.length + 1; ++j) {
let s = score(pattern, i - 1, word, j - i);
let match = gap;
if (lowPattern[i - 1] === lowWord[j - 1]) {
// some kind of match
if (pattern[i - 1] === word[j - 1]
&& (lowWord[j - 1] !== word[j - i] || i === j)
) {
// upper-case match or perfect match at start
match = 13;
table[i][j] = Math.max(
table[i - 1][j - 1] + s,
table[i - 1][j],
table[i][j - 1]
);
} else if (j > 1 && word[j - 2] === '_') {
// after separator
match = 11;
if (s > 0) {
oneMatch = true;
} else if (upPattern[i - 1] === word[j - 1]) {
// upper-case hit
match = 11;
} else {
// normal match
match = 1;
}
}
let diag = table[i - 1][j - 1] + match;
let top = table[i - 1][j] + gap;
let left = table[i][j - 1] + gap;
if (diag > top) {
if (diag > left) {
table[i][j] = diag;
} else {
table[i][j] = left;
}
} else {
if (top > left) {
table[i][j] = top;
} else {
table[i][j] = left;
}
}
if (match > topMatch) {
topMatch = match;
topMatchIdx = j - 1;
}
}
if (!oneMatch) {
if (topMatch < 0 || i !== 0 && topMatch === 1) {
// no match for first character or matched
// something unintersting in the middle
return undefined;
} else {
matches.push(topMatchIdx);
score += topMatch;
}
}
return [];
return [score, matches];
}
export function createMatches(position: number[]): IMatch[] {
......@@ -473,30 +517,30 @@ export function _matchRecursive(
return 0;
}
const lowPatternChar = lowPattern.charAt(patternPos);
const lowPatternChar = lowPattern[patternPos];
let idx = -1;
let value = 0;
if ((patternPos === wordPos
&& lowPatternChar === lowWord.charAt(wordPos))
&& lowPatternChar === lowWord[wordPos])
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, wordPos + 1, matches)) >= 0)
) {
matches.unshift(wordPos);
return (pattern.charAt(patternPos) === word.charAt(wordPos) ? 17 : 11) + value;
return (pattern[patternPos] === word[wordPos] ? 17 : 11) + value;
}
if ((idx = lowWord.indexOf(`_${lowPatternChar}`, wordPos)) >= 0
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 2, matches)) >= 0)
) {
matches.unshift(idx + 1);
return (pattern.charAt(patternPos) === word.charAt(idx + 1) ? 17 : 11) + value;
return (pattern[patternPos] === word[idx + 1] ? 17 : 11) + value;
}
if ((idx = word.indexOf(upPattern.charAt(patternPos), wordPos)) >= 0
if ((idx = word.indexOf(upPattern[patternPos], wordPos)) >= 0
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0)
) {
matches.unshift(idx);
return (pattern.charAt(patternPos) === word.charAt(idx) ? 17 : 11) + value;
return (pattern[patternPos] === word[idx] ? 17 : 11) + value;
}
if (patternPos > 0
......
......@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyMatchAndScore } from 'vs/base/common/filters';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyMatchAndScore, fuzzyLCS } from 'vs/base/common/filters';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst);
......@@ -195,8 +195,8 @@ suite('Filters', () => {
test('fuzzyMatchAndScore', function () {
function assertMatches(pattern: string, word: string, decoratedWord?: string) {
let r = fuzzyMatchAndScore(pattern, word);
function assertMatches(pattern: string, word: string, decoratedWord?: string, filter = fuzzyMatchAndScore) {
let r = filter(pattern, word);
assert.ok(Boolean(r) === Boolean(decoratedWord));
if (r) {
const [, matches] = r;
......@@ -234,6 +234,14 @@ suite('Filters', () => {
assertMatches('ccm', 'cacmelCase', '^ca^c^melCase');
assertMatches('ccm', 'camelCase');
assertMatches('ccm', 'camelCasecm', '^camel^Casec^m');
assertMatches('ob', 'foobar', undefined, fuzzyLCS);
assertMatches('BK', 'the_black_knight', 'the_^black_^knight', fuzzyLCS);
// assertMatches('BB', 'bakB', '^bak^B', fuzzyLCS);
// assertMatches('b', 'bakB', '^bakB', fuzzyLCS);
// assertMatches('B', 'bakB', 'bak^B', fuzzyLCS);
// assertMatches('ba', 'bakB', '^b^akB', fuzzyLCS);
// assertMatches('Ba', 'bakBa', 'bak^B^a', fuzzyLCS);
});
test('topScore', function () {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册