Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 8fd477a

Browse files
committed
Merge pull request #9615 from adobe/jeff/CodeHintsCaseBoost-Final
Add Case Boost to StringMatch
2 parents 0e1a7d2 + 70fe58c commit 8fd477a

2 files changed

Lines changed: 191 additions & 126 deletions

File tree

src/utils/StringMatch.js

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)