提交 16ece8e0 编写于 作者: J Johannes Rieken 提交者: Dirk Baeumer

make fuzzy match skip leading whitespace in pattern, fixes #26096

上级 7131a142
......@@ -448,6 +448,10 @@ _seps['\''] = true;
_seps['"'] = true;
_seps[':'] = true;
const _ws: { [ch: string]: boolean } = Object.create(null);
_ws[' '] = true;
_ws['\t'] = true;
const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 }
export function fuzzyScore(pattern: string, word: string): [number, number[]] {
......@@ -455,7 +459,20 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
const patternLen = pattern.length > 100 ? 100 : pattern.length;
const wordLen = word.length > 100 ? 100 : word.length;
if (patternLen === 0) {
// Check for leading whitespace in the pattern and
// start matching just after that position. This is
// like `pattern = pattern.rtrim()` but doesn't create
// a new string
let patternStartPos = 0;
for (const ch of pattern) {
if (_ws[ch]) {
patternStartPos += 1;
} else {
break;
}
}
if (patternLen === patternStartPos) {
return [-1, []];
}
......@@ -465,16 +482,17 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
const lowPattern = pattern.toLowerCase();
const lowWord = word.toLowerCase();
let i = 0;
let j = 0;
while (i < patternLen && j < wordLen) {
if (lowPattern[i] === lowWord[j]) {
i += 1;
let patternPos = patternStartPos;
let wordPos = 0;
while (patternPos < patternLen && wordPos < wordLen) {
if (lowPattern[patternPos] === lowWord[wordPos]) {
patternPos += 1;
}
j += 1;
wordPos += 1;
}
if (i !== patternLen) {
if (patternPos !== patternLen) {
// no simple matches found -> return early
return undefined;
}
......@@ -482,24 +500,24 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
// keep track of the maximum score
let maxScore = -1;
for (i = 1; i <= patternLen; i++) {
for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
let lastLowWordChar = '';
for (j = 1; j <= wordLen; j++) {
for (wordPos = 1; wordPos <= wordLen; wordPos++) {
let score = -1;
let lowWordChar = lowWord[j - 1];
if (lowPattern[i - 1] === lowWordChar) {
let lowWordChar = lowWord[wordPos - 1];
if (lowPattern[patternPos - 1] === lowWordChar) {
if (j === 1) {
if (pattern[i - 1] === word[j - 1]) {
if (wordPos === 1) {
if (pattern[patternPos - 1] === word[wordPos - 1]) {
score = 7;
} else {
score = 5;
}
} else if (lowWordChar !== word[j - 1]) {
if (pattern[i - 1] === word[j - 1]) {
} else if (lowWordChar !== word[wordPos - 1]) {
if (pattern[patternPos - 1] === word[wordPos - 1]) {
score = 7;
} else {
score = 5;
......@@ -512,38 +530,38 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
}
}
_scores[i][j] = score;
_scores[patternPos][wordPos] = score;
if (score > maxScore) {
maxScore = score;
}
let diag = _table[i - 1][j - 1] + (score > 1 ? 1 : score);
let top = _table[i - 1][j] + -1;
let left = _table[i][j - 1] + -1;
let diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score);
let top = _table[patternPos - 1][wordPos] + -1;
let left = _table[patternPos][wordPos - 1] + -1;
if (left >= top) {
// left or diag
if (left > diag) {
_table[i][j] = left;
_arrows[i][j] = Arrow.Left;
_table[patternPos][wordPos] = left;
_arrows[patternPos][wordPos] = Arrow.Left;
} else if (left === diag) {
_table[i][j] = left;
_arrows[i][j] = Arrow.Left | Arrow.Diag;
_table[patternPos][wordPos] = left;
_arrows[patternPos][wordPos] = Arrow.Left | Arrow.Diag;
} else {
_table[i][j] = diag;
_arrows[i][j] = Arrow.Diag;
_table[patternPos][wordPos] = diag;
_arrows[patternPos][wordPos] = Arrow.Diag;
}
} else {
// top or diag
if (top > diag) {
_table[i][j] = top;
_arrows[i][j] = Arrow.Top;
_table[patternPos][wordPos] = top;
_arrows[patternPos][wordPos] = Arrow.Top;
} else if (top === diag) {
_table[i][j] = top;
_arrows[i][j] = Arrow.Top | Arrow.Diag;
_table[patternPos][wordPos] = top;
_arrows[patternPos][wordPos] = Arrow.Top | Arrow.Diag;
} else {
_table[i][j] = diag;
_arrows[i][j] = Arrow.Diag;
_table[patternPos][wordPos] = diag;
_arrows[patternPos][wordPos] = Arrow.Diag;
}
}
......@@ -562,7 +580,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
}
let bucket: [number, number[]][] = [];
findAllMatches(patternLen, patternLen, wordLen, 0, [], bucket, false);
findAllMatches(patternLen, patternLen, patternStartPos, wordLen, 0, [], bucket, false);
if (bucket.length === 0) {
return undefined;
......@@ -580,7 +598,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
return topMatch;
}
function findAllMatches(patternLen: number, patternPos: number, wordPos: number, total: number, matches: number[], bucket: [number, number[]][], lastMatched: boolean): void {
function findAllMatches(patternLen: number, patternPos: number, patternStartPos: number, wordPos: number, total: number, matches: number[], bucket: [number, number[]][], lastMatched: boolean): void {
if (bucket.length >= 10) {
return;
......@@ -588,7 +606,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number,
let simpleMatchCount = 0;
while (patternPos > 0 && wordPos > 0) {
while (patternPos > patternStartPos && wordPos > 0) {
let score = _scores[patternPos][wordPos];
let arrow = _arrows[patternPos][wordPos];
......@@ -609,7 +627,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number,
if (arrow & Arrow.Left) {
// left
findAllMatches(
patternLen, patternPos,
patternLen, patternPos, patternStartPos,
wordPos - 1,
matches.length !== 0 ? total - 1 : total,
matches.slice(0), bucket, lastMatched
......@@ -635,7 +653,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number,
}
}
if (matches.length !== patternLen) {
if (matches.length !== patternLen - patternStartPos) {
// doesn't cover whole pattern
return undefined;
}
......
......@@ -195,7 +195,7 @@ suite('Filters', () => {
function assertMatches(pattern: string, word: string, decoratedWord: string, filter: typeof fuzzyScore) {
let r = filter(pattern, word);
assert.ok(Boolean(r) === Boolean(decoratedWord));
assert.ok(!decoratedWord === (!r || r[1].length === 0));
if (r) {
const [, matches] = r;
let pos = 0;
......@@ -325,6 +325,18 @@ suite('Filters', () => {
assertMatches('f', ':foo', ':^foo', fuzzyScore);
});
test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () {
assertMatches(' ', ' group', undefined, fuzzyScore);
assertMatches(' g', ' group', ' ^group', fuzzyScore);
assertMatches('g', ' group', ' ^group', fuzzyScore);
assertMatches('g g', ' groupGroup', undefined, fuzzyScore);
assertMatches('g g', ' group Group', ' ^group^ ^Group', fuzzyScore);
assertMatches(' g g', ' group Group', ' ^group^ ^Group', fuzzyScore);
assertMatches('zz', 'zzGroup', '^z^zGroup', fuzzyScore);
assertMatches('zzg', 'zzGroup', '^z^z^Group', fuzzyScore);
assertMatches('g', 'zzGroup', 'zz^Group', fuzzyScore);
});
function assertTopScore(filter: typeof fuzzyScore, pattern: string, expected: number, ...words: string[]) {
let topScore = -(100 * 10);
let topIdx = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册