Skip to content

Commit f0d7286

Browse files
committed
1 parent 4994c7b commit f0d7286

2 files changed

Lines changed: 90 additions & 81 deletions

File tree

src/vs/base/common/filters.ts

Lines changed: 86 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,14 @@ function printTable(table: number[][], pattern: string, patternLen: number, word
455455
return ret;
456456
}
457457

458+
function printTables(pattern: string, patternStart: number, word: string, wordStart: number): void {
459+
pattern = pattern.substr(patternStart);
460+
word = word.substr(wordStart);
461+
console.log(printTable(_table, pattern, pattern.length, word, word.length));
462+
console.log(printTable(_arrows, pattern, pattern.length, word, word.length));
463+
console.log(printTable(_scores, pattern, pattern.length, word, word.length));
464+
}
465+
458466
function isSeparatorAtPos(value: string, index: number): boolean {
459467
if (index < 0 || index >= value.length) {
460468
return false;
@@ -530,130 +538,127 @@ export interface FuzzyScorer {
530538
(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined;
531539
}
532540

533-
export function fuzzyScore(pattern: string, patternLow: string, patternPos: number, word: string, wordLow: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
534-
535-
if (patternPos > 0) {
536-
pattern = pattern.substr(patternPos);
537-
patternLow = patternLow.substr(patternPos);
538-
patternPos = 0;
539-
}
541+
export function fuzzyScore(pattern: string, patternLow: string, patternStart: number, word: string, wordLow: string, wordStart: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
540542

541543
const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length;
542544
const wordLen = word.length > _maxLen ? _maxLen : word.length;
543545

544-
if (patternPos >= patternLen || wordPos >= wordLen || patternLen > wordLen) {
546+
if (patternStart >= patternLen || wordStart >= wordLen || patternLen > wordLen) {
545547
return undefined;
546548
}
547549

548550
// Run a simple check if the characters of pattern occur
549551
// (in order) at all in word. If that isn't the case we
550552
// stop because no match will be possible
551-
if (!isPatternInWord(patternLow, patternPos, patternLen, wordLow, wordPos, wordLen)) {
553+
if (!isPatternInWord(patternLow, patternStart, patternLen, wordLow, wordStart, wordLen)) {
552554
return undefined;
553555
}
554556

555-
const patternStartPos = patternPos;
556-
const wordStartPos = wordPos;
557+
let row: number = 1;
558+
let column: number = 1;
559+
let patternPos = patternStart;
560+
let wordPos = wordStart;
557561

558562
// There will be a match, fill in tables
559-
for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
560-
561-
for (wordPos = 1; wordPos <= wordLen; wordPos++) {
562-
563-
let score = -1;
564-
if (patternLow[patternPos - 1] === wordLow[wordPos - 1]) {
563+
for (row = 1, patternPos = patternStart; patternPos < patternLen; row++ , patternPos++) {
565564

566-
if (wordPos === (patternPos - patternStartPos)) {
567-
// common prefix: `foobar <-> foobaz`
568-
// ^^^^^
569-
if (pattern[patternPos - 1] === word[wordPos - 1]) {
570-
score = 7;
571-
} else {
572-
score = 5;
573-
}
574-
} else if (isUpperCaseAtPos(wordPos - 1, word, wordLow) && (wordPos === 1 || !isUpperCaseAtPos(wordPos - 2, word, wordLow))) {
575-
// hitting upper-case: `foo <-> forOthers`
576-
// ^^ ^
577-
if (pattern[patternPos - 1] === word[wordPos - 1]) {
578-
score = 7;
579-
} else {
580-
score = 5;
581-
}
582-
} else if (isSeparatorAtPos(wordLow, wordPos - 1) && (wordPos === 1 || !isSeparatorAtPos(wordLow, wordPos - 2))) {
583-
// hitting a separator: `. <-> foo.bar`
584-
// ^
585-
score = 5;
586-
587-
} else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) {
588-
// post separator: `foo <-> bar_foo`
589-
// ^^^
590-
score = 5;
565+
for (column = 1, wordPos = wordStart; wordPos < wordLen; column++ , wordPos++) {
591566

592-
} else {
593-
score = 1;
594-
}
595-
}
567+
const score = _doScore(pattern, patternLow, patternPos, patternStart, word, wordLow, wordPos);
596568

597-
_scores[patternPos][wordPos] = score;
569+
_scores[row][column] = score;
598570

599-
const diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score);
600-
const top = _table[patternPos - 1][wordPos] + -1;
601-
const left = _table[patternPos][wordPos - 1] + -1;
571+
const diag = _table[row - 1][column - 1] + (score > 1 ? 1 : score);
572+
const top = _table[row - 1][column] + -1;
573+
const left = _table[row][column - 1] + -1;
602574

603575
if (left >= top) {
604576
// left or diag
605577
if (left > diag) {
606-
_table[patternPos][wordPos] = left;
607-
_arrows[patternPos][wordPos] = Arrow.Left;
578+
_table[row][column] = left;
579+
_arrows[row][column] = Arrow.Left;
608580
} else if (left === diag) {
609-
_table[patternPos][wordPos] = left;
610-
_arrows[patternPos][wordPos] = Arrow.Left | Arrow.Diag;
581+
_table[row][column] = left;
582+
_arrows[row][column] = Arrow.Left | Arrow.Diag;
611583
} else {
612-
_table[patternPos][wordPos] = diag;
613-
_arrows[patternPos][wordPos] = Arrow.Diag;
584+
_table[row][column] = diag;
585+
_arrows[row][column] = Arrow.Diag;
614586
}
615587
} else {
616588
// top or diag
617589
if (top > diag) {
618-
_table[patternPos][wordPos] = top;
619-
_arrows[patternPos][wordPos] = Arrow.Top;
590+
_table[row][column] = top;
591+
_arrows[row][column] = Arrow.Top;
620592
} else if (top === diag) {
621-
_table[patternPos][wordPos] = top;
622-
_arrows[patternPos][wordPos] = Arrow.Top | Arrow.Diag;
593+
_table[row][column] = top;
594+
_arrows[row][column] = Arrow.Top | Arrow.Diag;
623595
} else {
624-
_table[patternPos][wordPos] = diag;
625-
_arrows[patternPos][wordPos] = Arrow.Diag;
596+
_table[row][column] = diag;
597+
_arrows[row][column] = Arrow.Diag;
626598
}
627599
}
628600
}
629601
}
630602

631603
if (_debug) {
632-
console.log(printTable(_table, pattern, patternLen, word, wordLen));
633-
console.log(printTable(_arrows, pattern, patternLen, word, wordLen));
634-
console.log(printTable(_scores, pattern, patternLen, word, wordLen));
604+
printTables(pattern, patternStart, word, wordStart);
635605
}
636606

637607
_matchesCount = 0;
638608
_topScore = -100;
639-
_patternStartPos = patternStartPos;
609+
_wordStart = wordStart;
640610
_firstMatchCanBeWeak = firstMatchCanBeWeak;
641-
_findAllMatches2(patternLen, wordLen, patternLen === wordLen ? 1 : 0, 0, false);
611+
612+
_findAllMatches2(row - 1, column - 1, patternLen === wordLen ? 1 : 0, 0, false);
642613
if (_matchesCount === 0) {
643614
return undefined;
644615
}
645616

646-
return [_topScore, _topMatch2, wordStartPos];
617+
return [_topScore, _topMatch2, wordStart];
647618
}
648619

620+
function _doScore(pattern: string, patternLow: string, patternPos: number, patternStart: number, word: string, wordLow: string, wordPos: number) {
621+
if (patternLow[patternPos] !== wordLow[wordPos]) {
622+
return -1;
623+
}
624+
if (wordPos === (patternPos - patternStart)) {
625+
// common prefix: `foobar <-> foobaz`
626+
// ^^^^^
627+
if (pattern[patternPos] === word[wordPos]) {
628+
return 7;
629+
} else {
630+
return 5;
631+
}
632+
} else if (isUpperCaseAtPos(wordPos, word, wordLow) && (wordPos === 0 || !isUpperCaseAtPos(wordPos - 1, word, wordLow))) {
633+
// hitting upper-case: `foo <-> forOthers`
634+
// ^^ ^
635+
if (pattern[patternPos] === word[wordPos]) {
636+
return 7;
637+
} else {
638+
return 5;
639+
}
640+
} else if (isSeparatorAtPos(wordLow, wordPos) && (wordPos === 0 || !isSeparatorAtPos(wordLow, wordPos - 1))) {
641+
// hitting a separator: `. <-> foo.bar`
642+
// ^
643+
return 5;
644+
645+
} else if (isSeparatorAtPos(wordLow, wordPos - 1) || isWhitespaceAtPos(wordLow, wordPos - 1)) {
646+
// post separator: `foo <-> bar_foo`
647+
// ^^^
648+
return 5;
649+
650+
} else {
651+
return 1;
652+
}
653+
}
649654

650655
let _matchesCount: number = 0;
651656
let _topMatch2: number = 0;
652657
let _topScore: number = 0;
653-
let _patternStartPos: number = 0;
658+
let _wordStart: number = 0;
654659
let _firstMatchCanBeWeak: boolean = false;
655660

656-
function _findAllMatches2(patternPos: number, wordPos: number, total: number, matches: number, lastMatched: boolean): void {
661+
function _findAllMatches2(row: number, column: number, total: number, matches: number, lastMatched: boolean): void {
657662

658663
if (_matchesCount >= 10 || total < -25) {
659664
// stop when having already 10 results, or
@@ -663,14 +668,14 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma
663668

664669
let simpleMatchCount = 0;
665670

666-
while (patternPos > _patternStartPos && wordPos > 0) {
671+
while (row > 0 && column > 0) {
667672

668-
const score = _scores[patternPos][wordPos];
669-
const arrow = _arrows[patternPos][wordPos];
673+
const score = _scores[row][column];
674+
const arrow = _arrows[row][column];
670675

671676
if (arrow === Arrow.Left) {
672677
// left -> no match, skip a word character
673-
wordPos -= 1;
678+
column -= 1;
674679
if (lastMatched) {
675680
total -= 5; // new gap penalty
676681
} else if (matches !== 0) {
@@ -684,8 +689,8 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma
684689
if (arrow & Arrow.Left) {
685690
// left
686691
_findAllMatches2(
687-
patternPos,
688-
wordPos - 1,
692+
row,
693+
column - 1,
689694
matches !== 0 ? total - 1 : total, // gap penalty after first match
690695
matches,
691696
lastMatched
@@ -694,20 +699,20 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma
694699

695700
// diag
696701
total += score;
697-
patternPos -= 1;
698-
wordPos -= 1;
702+
row -= 1;
703+
column -= 1;
699704
lastMatched = true;
700705

701706
// match -> set a 1 at the word pos
702-
matches += 2 ** wordPos;
707+
matches += 2 ** (column + _wordStart);
703708

704709
// count simple matches and boost a row of
705710
// simple matches when they yield in a
706711
// strong match.
707712
if (score === 1) {
708713
simpleMatchCount += 1;
709714

710-
if (patternPos === _patternStartPos && !_firstMatchCanBeWeak) {
715+
if (row === 0 && !_firstMatchCanBeWeak) {
711716
// when the first match is a weak
712717
// match we discard it
713718
return undefined;
@@ -724,7 +729,7 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma
724729
}
725730
}
726731

727-
total -= wordPos >= 3 ? 9 : wordPos * 3; // late start penalty
732+
total -= column >= 3 ? 9 : column * 3; // late start penalty
728733

729734
// dynamically keep track of the current top score
730735
// and insert the current best score at head, the rest at tail

src/vs/base/test/common/filters.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ suite('Filters', () => {
392392
test('patternPos isn\'t working correctly #79815', function () {
393393
assertMatches(':p'.substr(1), 'prop', '^prop', fuzzyScore, { patternPos: 0 });
394394
assertMatches(':p', 'prop', '^prop', fuzzyScore, { patternPos: 1 });
395+
assertMatches(':p', 'prop', undefined, fuzzyScore, { patternPos: 2 });
396+
assertMatches(':p', 'proP', 'pro^P', fuzzyScore, { patternPos: 1, wordPos: 1 });
397+
assertMatches(':p', 'aprop', 'a^prop', fuzzyScore, { patternPos: 1, firstMatchCanBeWeak: true });
398+
assertMatches(':p', 'aprop', undefined, fuzzyScore, { patternPos: 1, firstMatchCanBeWeak: false });
395399
});
396400

397401
function assertTopScore(filter: typeof fuzzyScore, pattern: string, expected: number, ...words: string[]) {

0 commit comments

Comments
 (0)