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

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

上级 7131a142
...@@ -448,6 +448,10 @@ _seps['\''] = true; ...@@ -448,6 +448,10 @@ _seps['\''] = true;
_seps['"'] = true; _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 } const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 }
export function fuzzyScore(pattern: string, word: string): [number, number[]] { export function fuzzyScore(pattern: string, word: string): [number, number[]] {
...@@ -455,7 +459,20 @@ 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 patternLen = pattern.length > 100 ? 100 : pattern.length;
const wordLen = word.length > 100 ? 100 : word.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, []]; return [-1, []];
} }
...@@ -465,16 +482,17 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] { ...@@ -465,16 +482,17 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
const lowPattern = pattern.toLowerCase(); const lowPattern = pattern.toLowerCase();
const lowWord = word.toLowerCase(); const lowWord = word.toLowerCase();
let i = 0;
let j = 0;
while (i < patternLen && j < wordLen) { let patternPos = patternStartPos;
if (lowPattern[i] === lowWord[j]) { let wordPos = 0;
i += 1;
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 // no simple matches found -> return early
return undefined; return undefined;
} }
...@@ -482,24 +500,24 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] { ...@@ -482,24 +500,24 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
// keep track of the maximum score // keep track of the maximum score
let maxScore = -1; let maxScore = -1;
for (i = 1; i <= patternLen; i++) { for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
let lastLowWordChar = ''; let lastLowWordChar = '';
for (j = 1; j <= wordLen; j++) { for (wordPos = 1; wordPos <= wordLen; wordPos++) {
let score = -1; let score = -1;
let lowWordChar = lowWord[j - 1]; let lowWordChar = lowWord[wordPos - 1];
if (lowPattern[i - 1] === lowWordChar) { if (lowPattern[patternPos - 1] === lowWordChar) {
if (j === 1) { if (wordPos === 1) {
if (pattern[i - 1] === word[j - 1]) { if (pattern[patternPos - 1] === word[wordPos - 1]) {
score = 7; score = 7;
} else { } else {
score = 5; score = 5;
} }
} else if (lowWordChar !== word[j - 1]) { } else if (lowWordChar !== word[wordPos - 1]) {
if (pattern[i - 1] === word[j - 1]) { if (pattern[patternPos - 1] === word[wordPos - 1]) {
score = 7; score = 7;
} else { } else {
score = 5; score = 5;
...@@ -512,38 +530,38 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] { ...@@ -512,38 +530,38 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
} }
} }
_scores[i][j] = score; _scores[patternPos][wordPos] = score;
if (score > maxScore) { if (score > maxScore) {
maxScore = score; maxScore = score;
} }
let diag = _table[i - 1][j - 1] + (score > 1 ? 1 : score); let diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score);
let top = _table[i - 1][j] + -1; let top = _table[patternPos - 1][wordPos] + -1;
let left = _table[i][j - 1] + -1; let left = _table[patternPos][wordPos - 1] + -1;
if (left >= top) { if (left >= top) {
// left or diag // left or diag
if (left > diag) { if (left > diag) {
_table[i][j] = left; _table[patternPos][wordPos] = left;
_arrows[i][j] = Arrow.Left; _arrows[patternPos][wordPos] = Arrow.Left;
} else if (left === diag) { } else if (left === diag) {
_table[i][j] = left; _table[patternPos][wordPos] = left;
_arrows[i][j] = Arrow.Left | Arrow.Diag; _arrows[patternPos][wordPos] = Arrow.Left | Arrow.Diag;
} else { } else {
_table[i][j] = diag; _table[patternPos][wordPos] = diag;
_arrows[i][j] = Arrow.Diag; _arrows[patternPos][wordPos] = Arrow.Diag;
} }
} else { } else {
// top or diag // top or diag
if (top > diag) { if (top > diag) {
_table[i][j] = top; _table[patternPos][wordPos] = top;
_arrows[i][j] = Arrow.Top; _arrows[patternPos][wordPos] = Arrow.Top;
} else if (top === diag) { } else if (top === diag) {
_table[i][j] = top; _table[patternPos][wordPos] = top;
_arrows[i][j] = Arrow.Top | Arrow.Diag; _arrows[patternPos][wordPos] = Arrow.Top | Arrow.Diag;
} else { } else {
_table[i][j] = diag; _table[patternPos][wordPos] = diag;
_arrows[i][j] = Arrow.Diag; _arrows[patternPos][wordPos] = Arrow.Diag;
} }
} }
...@@ -562,7 +580,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] { ...@@ -562,7 +580,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
} }
let bucket: [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) { if (bucket.length === 0) {
return undefined; return undefined;
...@@ -580,7 +598,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] { ...@@ -580,7 +598,7 @@ export function fuzzyScore(pattern: string, word: string): [number, number[]] {
return topMatch; 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) { if (bucket.length >= 10) {
return; return;
...@@ -588,7 +606,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number, ...@@ -588,7 +606,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number,
let simpleMatchCount = 0; let simpleMatchCount = 0;
while (patternPos > 0 && wordPos > 0) { while (patternPos > patternStartPos && wordPos > 0) {
let score = _scores[patternPos][wordPos]; let score = _scores[patternPos][wordPos];
let arrow = _arrows[patternPos][wordPos]; let arrow = _arrows[patternPos][wordPos];
...@@ -609,7 +627,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number, ...@@ -609,7 +627,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number,
if (arrow & Arrow.Left) { if (arrow & Arrow.Left) {
// left // left
findAllMatches( findAllMatches(
patternLen, patternPos, patternLen, patternPos, patternStartPos,
wordPos - 1, wordPos - 1,
matches.length !== 0 ? total - 1 : total, matches.length !== 0 ? total - 1 : total,
matches.slice(0), bucket, lastMatched matches.slice(0), bucket, lastMatched
...@@ -635,7 +653,7 @@ function findAllMatches(patternLen: number, patternPos: number, wordPos: number, ...@@ -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 // doesn't cover whole pattern
return undefined; return undefined;
} }
......
...@@ -195,7 +195,7 @@ suite('Filters', () => { ...@@ -195,7 +195,7 @@ suite('Filters', () => {
function assertMatches(pattern: string, word: string, decoratedWord: string, filter: typeof fuzzyScore) { function assertMatches(pattern: string, word: string, decoratedWord: string, filter: typeof fuzzyScore) {
let r = filter(pattern, word); let r = filter(pattern, word);
assert.ok(Boolean(r) === Boolean(decoratedWord)); assert.ok(!decoratedWord === (!r || r[1].length === 0));
if (r) { if (r) {
const [, matches] = r; const [, matches] = r;
let pos = 0; let pos = 0;
...@@ -325,6 +325,18 @@ suite('Filters', () => { ...@@ -325,6 +325,18 @@ suite('Filters', () => {
assertMatches('f', ':foo', ':^foo', fuzzyScore); 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[]) { function assertTopScore(filter: typeof fuzzyScore, pattern: string, expected: number, ...words: string[]) {
let topScore = -(100 * 10); let topScore = -(100 * 10);
let topIdx = 0; let topIdx = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册