@@ -115,13 +115,15 @@ define(function (require, exports, module) {
115115 DEBUG_SCORES = ds ;
116116 }
117117
118+
118119 // Constants for scoring
119- var SPECIAL_POINTS = 35 ;
120+ var SPECIAL_POINTS = 40 ;
120121 var MATCH_POINTS = 10 ;
121- var LAST_SEGMENT_BOOST = 1 ;
122+ var MATCH_CASE_POINTS = 7 ; // Consecutive non-case matches have higher priority
123+ var CONSECUTIVE_MATCHES_POINTS = 8 ;
122124 var BEGINNING_OF_NAME_POINTS = 10 ;
125+ var LAST_SEGMENT_BOOST = 1 ;
123126 var DEDUCTION_FOR_LENGTH = 0.2 ;
124- var CONSECUTIVE_MATCHES_POINTS = 7 ;
125127 var NOT_STARTING_ON_SPECIAL_PENALTY = 25 ;
126128
127129 // Used in match lists to designate matches of "special" characters (see
@@ -134,6 +136,11 @@ define(function (require, exports, module) {
134136 function NormalMatch ( index ) {
135137 this . index = index ;
136138 }
139+
140+ // Used in match lists to designate any matched characters that are case-sensitive matches
141+ function CaseMatch ( index ) {
142+ this . index = index ;
143+ }
137144
138145 /*
139146 * Finds the best matches between the query and the string. The query is
@@ -191,8 +198,8 @@ define(function (require, exports, module) {
191198 * forward searching to resume
192199 *
193200 * * When `deadBranches[queryCounter] = strCounter` it means if we're still trying to match
194- * `queryStr [queryCounter]` and we get to `str[strCounter]`, there's no way we can match the
195- * remainer of `queryStr ` with the remainder of `str` -- either using specials-only or
201+ * `queryLower [queryCounter]` and we get to `str[strCounter]`, there's no way we can match the
202+ * remainer of `queryLower ` with the remainder of `str` -- either using specials-only or
196203 * full any-char matching.
197204 *
198205 * * We know this because deadBranches[] is set in backtrack(), and we don't get to backtrack() unless
@@ -205,18 +212,20 @@ define(function (require, exports, module) {
205212 *
206213 * @param {string } query the search string (generally lower cased)
207214 * @param {string } str the string to compare with (generally lower cased)
215+ * @param {string } originalQuery the "non-normalized" query string (used to detect case match priority)
216+ * @param {string } OriginalStr the "non-normalized" string to compare with (used to detect case match priority)
208217 * @param {Array } specials list of special indexes in str (from findSpecialCharacters)
209218 * @param {int } startingSpecial index into specials array to start scanning with
210219 * @return {Array.<SpecialMatch|NormalMatch> } matched indexes or null if no matches possible
211220 */
212- function _generateMatchList ( query , str , specials , startingSpecial ) {
221+ function _generateMatchList ( query , str , originalQuery , OriginalStr , specials , startingSpecial ) {
213222 var result = [ ] ;
214223
215224 // used to keep track of which special character we're testing now
216225 var specialsCounter = startingSpecial ;
217226
218227 // strCounter and queryCounter are the indexes used for pulling characters
219- // off of the str/compareStr and query.
228+ // off of the str/compareLower and query.
220229 var strCounter = specials [ startingSpecial ] ;
221230 var queryCounter ;
222231
@@ -338,8 +347,13 @@ define(function (require, exports, module) {
338347 // we look character by character for matches
339348 if ( query [ queryCounter ] === str [ strCounter ] ) {
340349 // got a match! record it, and switch back to searching specials
350+ if ( originalQuery [ queryCounter ] === OriginalStr [ strCounter ] ) {
351+ result . push ( new CaseMatch ( strCounter ++ ) ) ;
352+ } else {
353+ result . push ( new NormalMatch ( strCounter ++ ) ) ;
354+ }
355+
341356 queryCounter ++ ;
342- result . push ( new NormalMatch ( strCounter ++ ) ) ;
343357 state = SPECIALS_MATCH ;
344358 } else {
345359 // no match, keep looking
@@ -362,6 +376,7 @@ define(function (require, exports, module) {
362376 return result ;
363377 }
364378
379+
365380 /*
366381 * Seek out the best match in the last segment (generally the filename).
367382 * Matches in the filename are preferred, but the query entered could match
@@ -376,27 +391,33 @@ define(function (require, exports, module) {
376391 *
377392 * @param {string } query the search string (generally lower cased)
378393 * @param {string } str the string to compare with (generally lower cased)
394+ * @param {string } originalQuery the "non-normalized" query string (used to detect case match priority)
395+ * @param {string } OriginalStr the "non-normalized" string to compare with (used to detect case match priority)
379396 * @param {Array } specials list of special indexes in str (from findSpecialCharacters)
380397 * @param {int } startingSpecial index into specials array to start scanning with
381398 * @param {boolean } lastSegmentStart which character does the last segment start at
382399 * @return {{remainder:int, matchList:Array.<SpecialMatch|NormalMatch>} } matched indexes or null if no matches possible
383400 */
384- function _lastSegmentSearch ( query , str , specials , startingSpecial , lastSegmentStart ) {
401+ function _lastSegmentSearch ( query , str , originalQuery , OriginalStr , specials , startingSpecial , lastSegmentStart ) {
385402 var queryCounter , matchList ;
386403
387404 // It's possible that the query is longer than the last segment.
388405 // If so, we can chop off the bit that we know couldn't possibly be there.
389- var remainder = "" ;
390- var extraCharacters = specials [ startingSpecial ] + query . length - str . length ;
406+ var remainder = "" ,
407+ originalRemainder = "" ,
408+ extraCharacters = specials [ startingSpecial ] + query . length - str . length ;
391409
392410 if ( extraCharacters > 0 ) {
393411 remainder = query . substring ( 0 , extraCharacters ) ;
412+ originalRemainder = originalQuery . substring ( 0 , extraCharacters ) ;
394413 query = query . substring ( extraCharacters ) ;
414+ originalQuery = originalQuery . substring ( extraCharacters ) ;
395415 }
396416
397417 for ( queryCounter = 0 ; queryCounter < query . length ; queryCounter ++ ) {
398418 matchList = _generateMatchList ( query . substring ( queryCounter ) ,
399- str , specials , startingSpecial ) ;
419+ str , originalQuery . substring ( queryCounter ) ,
420+ OriginalStr , specials , startingSpecial ) ;
400421
401422 // if we've got a match *or* there are no segments in this string, we're done
402423 if ( matchList || startingSpecial === 0 ) {
@@ -409,6 +430,7 @@ define(function (require, exports, module) {
409430 } else {
410431 return {
411432 remainder : remainder + query . substring ( 0 , queryCounter ) ,
433+ originalRemainder : originalRemainder + originalQuery . substring ( 0 , queryCounter ) ,
412434 matchList : matchList
413435 } ;
414436 }
@@ -420,18 +442,20 @@ define(function (require, exports, module) {
420442 *
421443 * The parameters and return value are the same as for getMatchRanges.
422444 *
423- * @param {string } query the search string (will be searched lower case)
424- * @param {string } compareStr the lower-cased string to search
445+ * @param {string } queryLower the search string (will be searched lower case)
446+ * @param {string } compareLower the lower-cased string to search
447+ * @param {string } originalQuery the "non-normalized" query string (used to detect case match priority)
448+ * @param {string } OriginalStr the "non-normalized" string to compare with (used to detect case match priority)
425449 * @param {Array } specials list of special indexes in str (from findSpecialCharacters)
426450 * @param {int } lastSegmentSpecialsIndex index into specials array to start scanning with
427451 * @return {Array.<SpecialMatch|NormalMatch> } matched indexes or null if no matches possible
428452 */
429- function _wholeStringSearch ( query , compareStr , specials , lastSegmentSpecialsIndex ) {
453+ function _wholeStringSearch ( queryLower , compareLower , originalQuery , OriginalStr , specials , lastSegmentSpecialsIndex ) {
430454 var lastSegmentStart = specials [ lastSegmentSpecialsIndex ] ;
431455 var result ;
432456 var matchList ;
433457
434- result = _lastSegmentSearch ( query , compareStr , specials , lastSegmentSpecialsIndex , lastSegmentStart ) ;
458+ result = _lastSegmentSearch ( queryLower , compareLower , originalQuery , OriginalStr , specials , lastSegmentSpecialsIndex , lastSegmentStart ) ;
435459
436460 if ( result ) {
437461 matchList = result . matchList ;
@@ -440,7 +464,9 @@ define(function (require, exports, module) {
440464 if ( result . remainder ) {
441465 // Scan with the remainder only through the beginning of the last segment
442466 var remainderMatchList = _generateMatchList ( result . remainder ,
443- compareStr . substring ( 0 , lastSegmentStart ) ,
467+ compareLower . substring ( 0 , lastSegmentStart ) ,
468+ result . originalRemainder ,
469+ OriginalStr . substring ( 0 , lastSegmentStart ) ,
444470 specials . slice ( 0 , lastSegmentSpecialsIndex ) , 0 ) ;
445471
446472 if ( remainderMatchList ) {
@@ -454,7 +480,7 @@ define(function (require, exports, module) {
454480 } else {
455481 // No match in the last segment, so we start over searching the whole
456482 // string
457- matchList = _generateMatchList ( query , compareStr , specials , 0 ) ;
483+ matchList = _generateMatchList ( queryLower , compareLower , originalQuery , OriginalStr , specials , 0 ) ;
458484 }
459485
460486 return matchList ;
@@ -481,6 +507,7 @@ define(function (require, exports, module) {
481507 scoreDebug = {
482508 special : 0 ,
483509 match : 0 ,
510+ case : 0 ,
484511 lastSegment : 0 ,
485512 beginning : 0 ,
486513 lengthDeduction : 0 ,
@@ -544,6 +571,13 @@ define(function (require, exports, module) {
544571 }
545572 newPoints += MATCH_POINTS ;
546573
574+ if ( match instanceof CaseMatch ) {
575+ if ( DEBUG_SCORES ) {
576+ scoreDebug . case += MATCH_CASE_POINTS ;
577+ }
578+ newPoints += MATCH_CASE_POINTS ;
579+ }
580+
547581 // A bonus is given for characters that match at the beginning
548582 // of the filename
549583 if ( c === lastSegmentStart ) {
@@ -664,12 +698,20 @@ define(function (require, exports, module) {
664698 */
665699 function _prefixMatchResult ( str , query ) {
666700 var result = new SearchResult ( str ) ;
701+
667702 result . matchGoodness = - Number . MAX_VALUE ;
703+
704+ if ( str . substr ( 0 , query . length ) !== query ) {
705+ // Penalize for not matching case
706+ result . matchGoodness *= 0.5 ;
707+ }
708+
668709 if ( DEBUG_SCORES ) {
669710 result . scoreDebug = {
670- beginning : Number . MAX_VALUE
711+ beginning : - result . matchGoodness
671712 } ;
672713 }
714+
673715 result . stringRanges = [ {
674716 text : str . substr ( 0 , query . length ) ,
675717 matched : true ,
@@ -684,7 +726,8 @@ define(function (require, exports, module) {
684726 }
685727 return result ;
686728 }
687-
729+
730+
688731 /*
689732 * Match str against the query using the QuickOpen algorithm provided by
690733 * the functions above. The general idea is to prefer matches of "special" characters and,
@@ -732,14 +775,18 @@ define(function (require, exports, module) {
732775 }
733776
734777 // comparisons are case insensitive, so switch to lower case here
735- query = query . toLowerCase ( ) ;
736- var compareStr = str . toLowerCase ( ) ;
778+ var queryLower = query . toLowerCase ( ) ;
779+ var compareLower = str . toLowerCase ( ) ;
737780
738781 if ( options . preferPrefixMatches ) {
739782 options . segmentedSearch = false ;
740783 }
741784
742- if ( options . preferPrefixMatches && compareStr . substr ( 0 , query . length ) === query ) {
785+ if ( options . preferPrefixMatches && compareLower . substr ( 0 , queryLower . length ) === queryLower ) {
786+ // NOTE: we compare against the case insensitive match
787+ // above but we pass the case-sensitive version in
788+ // because we want to weight the match to give case-matches
789+ // a higher score
743790 return _prefixMatchResult ( str , query ) ;
744791 }
745792
@@ -754,14 +801,13 @@ define(function (require, exports, module) {
754801 // avoid some extra work
755802 if ( options . segmentedSearch ) {
756803 lastSegmentStart = special . specials [ special . lastSegmentSpecialsIndex ] ;
757- matchList = _wholeStringSearch ( query , compareStr , special . specials ,
804+ matchList = _wholeStringSearch ( queryLower , compareLower , query , str , special . specials ,
758805 special . lastSegmentSpecialsIndex ) ;
759806 } else {
760807 lastSegmentStart = 0 ;
761- matchList = _generateMatchList ( query , compareStr , special . specials ,
762- 0 ) ;
808+ matchList = _generateMatchList ( queryLower , compareLower , query , str , special . specials , 0 ) ;
763809 }
764-
810+
765811 // If we get a match, turn this into a SearchResult as expected by the consumers
766812 // of this API.
767813 if ( matchList ) {
@@ -930,6 +976,7 @@ define(function (require, exports, module) {
930976 exports . _generateMatchList = _generateMatchList ;
931977 exports . _SpecialMatch = SpecialMatch ;
932978 exports . _NormalMatch = NormalMatch ;
979+ exports . _CaseMatch = CaseMatch ;
933980 exports . _computeRangesAndScore = _computeRangesAndScore ;
934981
935982 // public exports
0 commit comments