Skip to content

Commit 53d1444

Browse files
committed
8244535: JavaDoc search is overly strict with letter case
Reviewed-by: jjg
1 parent 78fdb65 commit 53d1444

File tree

5 files changed

+149
-135
lines changed

5 files changed

+149
-135
lines changed

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js

Lines changed: 80 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,22 @@ var catMembers = "Members";
3232
var catSearchTags = "SearchTags";
3333
var highlight = "<span class=\"result-highlight\">$&</span>";
3434
var searchPattern = "";
35+
var fallbackPattern = "";
3536
var RANKING_THRESHOLD = 2;
3637
var NO_MATCH = 0xffff;
37-
var MAX_RESULTS_PER_CATEGORY = 500;
38+
var MIN_RESULTS = 3;
39+
var MAX_RESULTS = 500;
3840
var UNNAMED = "<Unnamed>";
3941
function escapeHtml(str) {
4042
return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
4143
}
42-
function getHighlightedText(item, matcher) {
44+
function getHighlightedText(item, matcher, fallbackMatcher) {
4345
var escapedItem = escapeHtml(item);
44-
return escapedItem.replace(matcher, highlight);
46+
var highlighted = escapedItem.replace(matcher, highlight);
47+
if (highlighted === escapedItem) {
48+
highlighted = escapedItem.replace(fallbackMatcher, highlight)
49+
}
50+
return highlighted;
4551
}
4652
function getURLPrefix(ui) {
4753
var urlPrefix="";
@@ -60,11 +66,10 @@ function getURLPrefix(ui) {
6066
}
6167
});
6268
}
63-
return urlPrefix;
6469
}
6570
return urlPrefix;
6671
}
67-
function makeCamelCaseRegex(term) {
72+
function createSearchPattern(term) {
6873
var pattern = "";
6974
var isWordToken = false;
7075
term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) {
@@ -93,26 +98,26 @@ function createMatcher(pattern, flags) {
9398
}
9499
var watermark = 'Search';
95100
$(function() {
96-
$("#search").val('');
97-
$("#search").prop("disabled", false);
98-
$("#reset").prop("disabled", false);
99-
$("#search").val(watermark).addClass('watermark');
100-
$("#search").blur(function() {
101-
if ($(this).val().length == 0) {
101+
var search = $("#search");
102+
var reset = $("#reset");
103+
search.val('');
104+
search.prop("disabled", false);
105+
reset.prop("disabled", false);
106+
search.val(watermark).addClass('watermark');
107+
search.blur(function() {
108+
if ($(this).val().length === 0) {
102109
$(this).val(watermark).addClass('watermark');
103110
}
104111
});
105-
$("#search").on('click keydown paste', function() {
106-
if ($(this).val() == watermark) {
112+
search.on('click keydown paste', function() {
113+
if ($(this).val() === watermark) {
107114
$(this).val('').removeClass('watermark');
108115
}
109116
});
110-
$("#reset").click(function() {
111-
$("#search").val('');
112-
$("#search").focus();
117+
reset.click(function() {
118+
search.val('').focus();
113119
});
114-
$("#search").focus();
115-
$("#search")[0].setSelectionRange(0, 0);
120+
search.focus()[0].setSelectionRange(0, 0);
116121
});
117122
$.widget("custom.catcomplete", $.ui.autocomplete, {
118123
_create: function() {
@@ -142,20 +147,21 @@ $.widget("custom.catcomplete", $.ui.autocomplete, {
142147
_renderItem: function(ul, item) {
143148
var label = "";
144149
var matcher = createMatcher(escapeHtml(searchPattern), "g");
150+
var fallbackMatcher = new RegExp(fallbackPattern, "gi")
145151
if (item.category === catModules) {
146-
label = getHighlightedText(item.l, matcher);
152+
label = getHighlightedText(item.l, matcher, fallbackMatcher);
147153
} else if (item.category === catPackages) {
148-
label = getHighlightedText(item.l, matcher);
154+
label = getHighlightedText(item.l, matcher, fallbackMatcher);
149155
} else if (item.category === catTypes) {
150156
label = (item.p && item.p !== UNNAMED)
151-
? getHighlightedText(item.p + "." + item.l, matcher)
152-
: getHighlightedText(item.l, matcher);
157+
? getHighlightedText(item.p + "." + item.l, matcher, fallbackMatcher)
158+
: getHighlightedText(item.l, matcher, fallbackMatcher);
153159
} else if (item.category === catMembers) {
154160
label = (item.p && item.p !== UNNAMED)
155-
? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher)
156-
: getHighlightedText(item.c + "." + item.l, matcher);
161+
? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher, fallbackMatcher)
162+
: getHighlightedText(item.c + "." + item.l, matcher, fallbackMatcher);
157163
} else if (item.category === catSearchTags) {
158-
label = getHighlightedText(item.l, matcher);
164+
label = getHighlightedText(item.l, matcher, fallbackMatcher);
159165
} else {
160166
label = item.l;
161167
}
@@ -186,25 +192,25 @@ function rankMatch(match, category) {
186192
var input = match.input;
187193
var leftBoundaryMatch = 2;
188194
var periferalMatch = 0;
189-
var delta = 0;
190195
// make sure match is anchored on a left word boundary
191-
if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index - 1] || "_" === input[index]) {
196+
if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index]) {
192197
leftBoundaryMatch = 0;
193-
} else if (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input)) {
198+
} else if ("_" === input[index - 1] || (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input))) {
194199
leftBoundaryMatch = 1;
195200
}
196201
var matchEnd = index + match[0].length;
197202
var leftParen = input.indexOf("(");
203+
var endOfName = leftParen > -1 ? leftParen : input.length;
198204
// exclude peripheral matches
199205
if (category !== catModules && category !== catSearchTags) {
200-
var endOfName = leftParen > -1 ? leftParen : input.length;
201206
var delim = category === catPackages ? "/" : ".";
202207
if (leftParen > -1 && leftParen < index) {
203208
periferalMatch += 2;
204209
} else if (input.lastIndexOf(delim, endOfName) >= matchEnd) {
205210
periferalMatch += 2;
206211
}
207212
}
213+
var delta = match[0].length === endOfName ? 0 : 1; // rank full match higher than partial match
208214
for (var i = 1; i < match.length; i++) {
209215
// lower ranking if parts of the name are missing
210216
if (match[i])
@@ -222,88 +228,58 @@ function rankMatch(match, category) {
222228
}
223229
function doSearch(request, response) {
224230
var result = [];
225-
var newResults = [];
226-
227-
searchPattern = makeCamelCaseRegex(request.term);
231+
searchPattern = createSearchPattern(request.term);
232+
fallbackPattern = createSearchPattern(request.term.toLowerCase());
228233
if (searchPattern === "") {
229234
return this.close();
230235
}
231236
var camelCaseMatcher = createMatcher(searchPattern, "");
232-
var boundaryMatcher = createMatcher("\\b" + searchPattern, "");
237+
var fallbackMatcher = new RegExp(fallbackPattern, "i");
233238

234-
function concatResults(a1, a2) {
235-
a2.sort(function(e1, e2) {
236-
return e1.ranking - e2.ranking;
237-
});
238-
a1 = a1.concat(a2.map(function(e) { return e.item; }));
239-
a2.length = 0;
240-
return a1;
241-
}
242-
243-
if (moduleSearchIndex) {
244-
$.each(moduleSearchIndex, function(index, item) {
245-
item.category = catModules;
246-
var ranking = rankMatch(boundaryMatcher.exec(item.l), catModules);
247-
if (ranking < RANKING_THRESHOLD) {
248-
newResults.push({ ranking: ranking, item: item });
249-
}
250-
return newResults.length < MAX_RESULTS_PER_CATEGORY;
251-
});
252-
result = concatResults(result, newResults);
253-
}
254-
if (packageSearchIndex) {
255-
$.each(packageSearchIndex, function(index, item) {
256-
item.category = catPackages;
257-
var name = (item.m && request.term.indexOf("/") > -1)
258-
? (item.m + "/" + item.l)
259-
: item.l;
260-
var ranking = rankMatch(boundaryMatcher.exec(name), catPackages);
261-
if (ranking < RANKING_THRESHOLD) {
262-
newResults.push({ ranking: ranking, item: item });
263-
}
264-
return newResults.length < MAX_RESULTS_PER_CATEGORY;
265-
});
266-
result = concatResults(result, newResults);
267-
}
268-
if (typeSearchIndex) {
269-
$.each(typeSearchIndex, function(index, item) {
270-
item.category = catTypes;
271-
var name = request.term.indexOf(".") > -1
272-
? item.p + "." + item.l
273-
: item.l;
274-
var ranking = rankMatch(camelCaseMatcher.exec(name), catTypes);
275-
if (ranking < RANKING_THRESHOLD) {
276-
newResults.push({ ranking: ranking, item: item });
277-
}
278-
return newResults.length < MAX_RESULTS_PER_CATEGORY;
279-
});
280-
result = concatResults(result, newResults);
281-
}
282-
if (memberSearchIndex) {
283-
$.each(memberSearchIndex, function(index, item) {
284-
item.category = catMembers;
285-
var name = request.term.indexOf(".") > -1
286-
? item.p + "." + item.c + "." + item.l
287-
: item.l;
288-
var ranking = rankMatch(camelCaseMatcher.exec(name), catMembers);
289-
if (ranking < RANKING_THRESHOLD) {
290-
newResults.push({ ranking: ranking, item: item });
291-
}
292-
return newResults.length < MAX_RESULTS_PER_CATEGORY;
293-
});
294-
result = concatResults(result, newResults);
239+
function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) {
240+
if (indexArray) {
241+
var newResults = [];
242+
$.each(indexArray, function (i, item) {
243+
item.category = category;
244+
var ranking = rankMatch(matcher.exec(nameFunc(item)), category);
245+
if (ranking < RANKING_THRESHOLD) {
246+
newResults.push({ranking: ranking, item: item});
247+
}
248+
return newResults.length <= MAX_RESULTS;
249+
});
250+
return newResults.sort(function(e1, e2) {
251+
return e1.ranking - e2.ranking;
252+
}).map(function(e) {
253+
return e.item;
254+
});
255+
}
256+
return [];
295257
}
296-
if (tagSearchIndex) {
297-
$.each(tagSearchIndex, function(index, item) {
298-
item.category = catSearchTags;
299-
var ranking = rankMatch(boundaryMatcher.exec(item.l), catSearchTags);
300-
if (ranking < RANKING_THRESHOLD) {
301-
newResults.push({ ranking: ranking, item: item });
302-
}
303-
return newResults.length < MAX_RESULTS_PER_CATEGORY;
304-
});
305-
result = concatResults(result, newResults);
258+
function searchIndex(indexArray, category, nameFunc) {
259+
var primaryResults = searchIndexWithMatcher(indexArray, camelCaseMatcher, category, nameFunc);
260+
result = result.concat(primaryResults);
261+
if (primaryResults.length <= MIN_RESULTS && camelCaseMatcher.flags.indexOf("i") === -1) {
262+
var secondaryResults = searchIndexWithMatcher(indexArray, fallbackMatcher, category, nameFunc);
263+
result = result.concat(secondaryResults.filter(function (item) {
264+
return primaryResults.indexOf(item) === -1;
265+
}));
266+
}
306267
}
268+
269+
searchIndex(moduleSearchIndex, catModules, function(item) { return item.l; });
270+
searchIndex(packageSearchIndex, catPackages, function(item) {
271+
return (item.m && request.term.indexOf("/") > -1)
272+
? (item.m + "/" + item.l) : item.l;
273+
});
274+
searchIndex(typeSearchIndex, catTypes, function(item) {
275+
return request.term.indexOf(".") > -1 ? item.p + "." + item.l : item.l;
276+
});
277+
searchIndex(memberSearchIndex, catMembers, function(item) {
278+
return request.term.indexOf(".") > -1
279+
? item.p + "." + item.c + "." + item.l : item.l;
280+
});
281+
searchIndex(tagSearchIndex, catSearchTags, function(item) { return item.l; });
282+
307283
if (!indexFilesLoaded()) {
308284
updateSearchResults = function() {
309285
doSearch(request, response);

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ public void add(IndexItem item) {
172172
}
173173

174174
itemsByCategory.computeIfAbsent(item.getCategory(),
175-
c -> new TreeSet<>(mainComparator))
175+
c -> new TreeSet<>(c == IndexItem.Category.TYPES
176+
? makeTypeSearchIndexComparator()
177+
: makeGenericSearchIndexComparator()))
176178
.add(item);
177179
}
178180

@@ -344,4 +346,28 @@ private Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
344346
};
345347
}
346348

349+
/**
350+
* Returns a Comparator for IndexItems in the types category of the search index.
351+
* Items are compared by short name, falling back to the main comparator if names are equal.
352+
*
353+
* @return a Comparator
354+
*/
355+
public Comparator<IndexItem> makeTypeSearchIndexComparator() {
356+
Comparator<IndexItem> simpleNameComparator =
357+
(ii1, ii2) -> utils.compareStrings(ii1.getSimpleName(), ii2.getSimpleName());
358+
return simpleNameComparator.thenComparing(mainComparator);
359+
}
360+
361+
/**
362+
* Returns a Comparator for IndexItems in the modules, packages, members, and search tags
363+
* categories of the search index.
364+
* Items are compared by label, falling back to the main comparator if names are equal.
365+
*
366+
* @return a Comparator
367+
*/
368+
public Comparator<IndexItem> makeGenericSearchIndexComparator() {
369+
Comparator<IndexItem> labelComparator =
370+
(ii1, ii2) -> utils.compareStrings(ii1.getLabel(), ii2.getLabel());
371+
return labelComparator.thenComparing(mainComparator);
372+
}
347373
}

test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,10 @@ void checkJqueryAndImageFiles(boolean expectedOutput) {
712712

713713
void checkSearchJS() {
714714
checkOutput("search.js", true,
715-
"function concatResults(a1, a2) {",
715+
"function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) {",
716716
"""
717-
$("#search").on('click keydown paste', function() {
718-
if ($(this).val() == watermark) {
717+
search.on('click keydown paste', function() {
718+
if ($(this).val() === watermark) {
719719
$(this).val('').removeClass('watermark');
720720
}
721721
});""",
@@ -737,7 +737,6 @@ function getURLPrefix(ui) {
737737
}
738738
});
739739
}
740-
return urlPrefix;
741740
}
742741
return urlPrefix;
743742
}""",

0 commit comments

Comments
 (0)