@@ -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+
458466function 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
650655let _matchesCount : number = 0 ;
651656let _topMatch2 : number = 0 ;
652657let _topScore : number = 0 ;
653- let _patternStartPos : number = 0 ;
658+ let _wordStart : number = 0 ;
654659let _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
0 commit comments