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

Commit 76dbae8

Browse files
committed
Merge pull request #2062 from adobe/pflynn/quickopen-highlighting
Fix Quick Open so discontiguous matches are highlighted in all modes. Fix bug where stringMatch() returns ranges missing the first char if the 1st char in unmatched while the 2nd char IS matched.
2 parents 7b0d56b + 7e53828 commit 76dbae8

File tree

1 file changed

+60
-47
lines changed

1 file changed

+60
-47
lines changed

src/search/QuickOpen.js

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,12 @@ define(function (require, exports, module) {
451451
* for sorting. It also has a stringRanges array, each entry with
452452
* "text", "matched" and "segment". If you string the "text" properties together, you will
453453
* get the original str. Using the matched property, you can highlight
454-
* the string matches. The segment property tells you the most specific segment
455-
* covered by the range, though there may be more than one segment convered.
454+
* the string matches. The segment property tells you the most specific (rightmost)
455+
* segment covered by the range, though there may be more than one segment covered.
456+
* Segments are currently determined by "/"s.
456457
*
457-
* Use basicMatchSort() to sort the filtered results taking this ranking
458-
* label of the SearchResult is set to 'str'.
458+
* Use basicMatchSort() to sort the filtered results taking this ranking into account.
459+
* The label of the SearchResult is set to 'str'.
459460
* @param {!string} str
460461
* @param {!string} query
461462
* @return {?SearchResult}
@@ -493,7 +494,7 @@ define(function (require, exports, module) {
493494
}
494495

495496
// No query? Short circuit the normal work done and just
496-
// return a simple match with a range that covers the whole string.
497+
// return a single range that covers the whole string.
497498
if (!query) {
498499
result = new SearchResult(str);
499500
result.matchGoodness = 0;
@@ -569,7 +570,7 @@ define(function (require, exports, module) {
569570
addToStringRanges(Math.abs(sequentialMatches), sequentialMatches > 0);
570571
}
571572

572-
if (strCounter > 0) {
573+
if (strCounter >= 0) {
573574
stringRanges.unshift({
574575
text: str.substring(0, strCounter + 1),
575576
matched: false,
@@ -644,7 +645,7 @@ define(function (require, exports, module) {
644645
* within each tier.
645646
*/
646647
function basicMatchSort(searchResults) {
647-
multiFieldSort(searchResults, { matchGoodness: 0, label: 2 });
648+
multiFieldSort(searchResults, { matchGoodness: 0, label: 1 });
648649
}
649650

650651

@@ -713,55 +714,66 @@ define(function (require, exports, module) {
713714
}
714715

715716

716-
function defaultResultsFormatter(item, query) {
717-
query = query.slice(query.indexOf("@") + 1, query.length);
718-
719-
// Escape both query and item so the replace works properly below
720-
query = StringUtils.htmlEscape(query);
717+
/**
718+
* Formats item's label as properly escaped HTML text, highlighting sections that match 'query'.
719+
* If item is a SearchResult generated by stringMatch(), uses its metadata about which string ranges
720+
* matched; else formats the label with no highlighting.
721+
* @param {!string|SearchResult} item
722+
* @param {?string} matchClass CSS class for highlighting matched text
723+
* @param {?function(number, string):string} rangeFilter
724+
* @return {!string} bolded, HTML-escaped result
725+
*/
726+
function highlightMatch(item, matchClass, rangeFilter) {
721727
var label = item.label || item;
722-
var displayName = StringUtils.htmlEscape(label);
723-
724-
if (query.length > 0) {
725-
// make the user's query bold within the item's text
726-
displayName = displayName.replace(
727-
new RegExp(StringUtils.regexEscape(query), "gi"),
728-
"<strong>$&</strong>"
729-
);
728+
matchClass = matchClass || "quicksearch-namematch";
729+
730+
var stringRanges = item.stringRanges;
731+
if (!stringRanges) {
732+
// If result didn't come from stringMatch(), highlight nothing
733+
stringRanges = [{
734+
text: label,
735+
matched: false,
736+
segment: 0
737+
}];
730738
}
731-
732-
return "<li>" + displayName + "</li>";
733-
}
734-
735-
function _filenameResultsFormatter(item, query) {
736-
// Use the filename formatter
737-
query = StringUtils.htmlEscape(query);
738-
739-
// put the path pieces together, highlighting the matched parts
740-
// of the string
739+
741740
var displayName = "";
742-
var displayPath = "";
743741

744-
item.stringRanges.forEach(function (range) {
742+
// Put the path pieces together, highlighting the matched parts
743+
stringRanges.forEach(function (range) {
745744
if (range.matched) {
746-
displayPath += '<span class="quicksearch-pathmatch">';
747-
displayName += '<span class="quicksearch-namematch">';
748-
} else {
749-
displayPath += '<span>';
750-
displayName += '<span>';
745+
displayName += "<span class='" + matchClass + "'>";
751746
}
752-
displayPath += StringUtils.breakableUrl(StringUtils.htmlEscape(range.text));
753-
displayPath += '</span>';
754747

755-
if (range.segment === 0) {
756-
var rightmostSlash = range.text.lastIndexOf('/');
757-
if (rightmostSlash > -1) {
758-
displayName += StringUtils.htmlEscape(range.text.substring(rightmostSlash + 1));
759-
} else {
760-
displayName += StringUtils.htmlEscape(range.text);
761-
}
748+
var rangeText = rangeFilter ? rangeFilter(range.segment, range.text) : range.text;
749+
displayName += StringUtils.breakableUrl(StringUtils.htmlEscape(rangeText));
750+
751+
if (range.matched) {
752+
displayName += "</span>";
762753
}
763-
displayName += '</span>';
764754
});
755+
return displayName;
756+
}
757+
758+
function defaultResultsFormatter(item, query) {
759+
query = query.slice(query.indexOf("@") + 1, query.length);
760+
761+
var displayName = highlightMatch(item);
762+
return "<li>" + displayName + "</li>";
763+
}
764+
765+
function _filenameResultsFormatter(item, query) {
766+
// For main label, we just want filename: drop most of the string
767+
function fileNameFilter(segment, rangeText) {
768+
if (segment === 0) {
769+
var rightmostSlash = rangeText.lastIndexOf('/');
770+
return rangeText.substring(rightmostSlash + 1); // safe even if rightmostSlash is -1
771+
} else {
772+
return "";
773+
}
774+
}
775+
var displayName = highlightMatch(item, null, fileNameFilter);
776+
var displayPath = highlightMatch(item, "quicksearch-pathmatch");
765777

766778
return "<li>" + displayName + "<br /><span class='quick-open-path'>" + displayPath + "</span></li>";
767779
}
@@ -938,4 +950,5 @@ define(function (require, exports, module) {
938950
exports.stringMatch = stringMatch;
939951
exports.basicMatchSort = basicMatchSort;
940952
exports.multiFieldSort = multiFieldSort;
953+
exports.highlightMatch = highlightMatch;
941954
});

0 commit comments

Comments
 (0)