mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-17 03:13:11 +00:00
8282559: Allow multiple search terms in javadoc search
Reviewed-by: jjg
This commit is contained in:
parent
7022543fcf
commit
5ac7186c9e
@ -50,9 +50,9 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
public class HelpWriter extends HtmlDocletWriter {
|
||||
|
||||
private final String[][] SEARCH_EXAMPLES = {
|
||||
{"j.l.obj", "\"java.lang.Object\""},
|
||||
{"InpStr", "\"java.io.InputStream\""},
|
||||
{"HM.cK", "\"java.util.HashMap.containsKey(Object)\""}
|
||||
{"\"j.l.obj\"", "\"java.lang.Object\""},
|
||||
{"\"InpStr\"", "\"java.io.InputStream\""},
|
||||
{"\"math exact long\"", "\"java.lang.Math.absExact(long)\""}
|
||||
};
|
||||
|
||||
Content overviewLink;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,262 +22,342 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
var noResult = {l: "##REPLACE:doclet.search.no_results##"};
|
||||
var loading = {l: "##REPLACE:doclet.search.loading##"};
|
||||
var catModules = "##REPLACE:doclet.search.modules##";
|
||||
var catPackages = "##REPLACE:doclet.search.packages##";
|
||||
var catTypes = "##REPLACE:doclet.search.types##";
|
||||
var catMembers = "##REPLACE:doclet.search.members##";
|
||||
var catSearchTags = "##REPLACE:doclet.search.search_tags##";
|
||||
var highlight = "<span class=\"result-highlight\">$&</span>";
|
||||
var searchPattern = "";
|
||||
var fallbackPattern = "";
|
||||
var RANKING_THRESHOLD = 2;
|
||||
var NO_MATCH = 0xffff;
|
||||
var MIN_RESULTS = 3;
|
||||
var MAX_RESULTS = 500;
|
||||
var UNNAMED = "<Unnamed>";
|
||||
"use strict";
|
||||
const messages = {
|
||||
noResult: "##REPLACE:doclet.search.no_results##",
|
||||
loading: "##REPLACE:doclet.search.loading##"
|
||||
}
|
||||
const categories = {
|
||||
modules: "##REPLACE:doclet.search.modules##",
|
||||
packages: "##REPLACE:doclet.search.packages##",
|
||||
types: "##REPLACE:doclet.search.types##",
|
||||
members: "##REPLACE:doclet.search.members##",
|
||||
searchTags: "##REPLACE:doclet.search.search_tags##"
|
||||
};
|
||||
const highlight = "<span class='result-highlight'>$&</span>";
|
||||
const NO_MATCH = {};
|
||||
const MAX_RESULTS = 1200;
|
||||
function checkUnnamed(name, separator) {
|
||||
return name === "<Unnamed>" || !name ? "" : name + separator;
|
||||
}
|
||||
function escapeHtml(str) {
|
||||
return str.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
function getHighlightedText(item, matcher, fallbackMatcher) {
|
||||
var escapedItem = escapeHtml(item);
|
||||
var highlighted = escapedItem.replace(matcher, highlight);
|
||||
if (highlighted === escapedItem) {
|
||||
highlighted = escapedItem.replace(fallbackMatcher, highlight)
|
||||
function getHighlightedText(str, boundaries, from, to) {
|
||||
var start = from;
|
||||
var text = "";
|
||||
for (var i = 0; i < boundaries.length; i += 2) {
|
||||
var b0 = boundaries[i];
|
||||
var b1 = boundaries[i + 1];
|
||||
if (b0 >= to || b1 <= from) {
|
||||
continue;
|
||||
}
|
||||
text += escapeHtml(str.slice(start, Math.max(start, b0)));
|
||||
text += "<span class='result-highlight'>";
|
||||
text += escapeHtml(str.slice(Math.max(start, b0), Math.min(to, b1)));
|
||||
text += "</span>";
|
||||
start = Math.min(to, b1);
|
||||
}
|
||||
return highlighted;
|
||||
text += escapeHtml(str.slice(start, to));
|
||||
return text;
|
||||
}
|
||||
function getURLPrefix(ui) {
|
||||
var urlPrefix="";
|
||||
function getURLPrefix(item, category) {
|
||||
var urlPrefix = "";
|
||||
var slash = "/";
|
||||
if (ui.item.category === catModules) {
|
||||
return ui.item.l + slash;
|
||||
} else if (ui.item.category === catPackages && ui.item.m) {
|
||||
return ui.item.m + slash;
|
||||
} else if (ui.item.category === catTypes || ui.item.category === catMembers) {
|
||||
if (ui.item.m) {
|
||||
urlPrefix = ui.item.m + slash;
|
||||
if (category === "modules") {
|
||||
return item.l + slash;
|
||||
} else if (category === "packages" && item.m) {
|
||||
return item.m + slash;
|
||||
} else if (category === "types" || category === "members") {
|
||||
if (item.m) {
|
||||
urlPrefix = item.m + slash;
|
||||
} else {
|
||||
$.each(packageSearchIndex, function(index, item) {
|
||||
if (item.m && ui.item.p === item.l) {
|
||||
urlPrefix = item.m + slash;
|
||||
$.each(packageSearchIndex, function(index, it) {
|
||||
if (it.m && item.p === it.l) {
|
||||
urlPrefix = it.m + slash;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return urlPrefix;
|
||||
}
|
||||
function createSearchPattern(term) {
|
||||
var pattern = "";
|
||||
var isWordToken = false;
|
||||
term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) {
|
||||
if (index > 0) {
|
||||
// whitespace between identifiers is significant
|
||||
pattern += (isWordToken && /^\w/.test(w)) ? "\\s+" : "\\s*";
|
||||
function getURL(item, category) {
|
||||
if (item.url) {
|
||||
return item.url;
|
||||
}
|
||||
var url = getURLPrefix(item, category);
|
||||
if (category === "modules") {
|
||||
url += "module-summary.html";
|
||||
} else if (category === "packages") {
|
||||
if (item.u) {
|
||||
url = item.u;
|
||||
} else {
|
||||
url += item.l.replace(/\./g, '/') + "/package-summary.html";
|
||||
}
|
||||
var tokens = w.split(/(?=[A-Z,.()<>[\/])/);
|
||||
} else if (category === "types") {
|
||||
if (item.u) {
|
||||
url = item.u;
|
||||
} else {
|
||||
url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.l + ".html";
|
||||
}
|
||||
} else if (category === "members") {
|
||||
url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.c + ".html" + "#";
|
||||
if (item.u) {
|
||||
url += item.u;
|
||||
} else {
|
||||
url += item.l;
|
||||
}
|
||||
} else if (category === "searchTags") {
|
||||
url += item.u;
|
||||
}
|
||||
item.url = url;
|
||||
return url;
|
||||
}
|
||||
function createMatcher(term, camelCase) {
|
||||
if (camelCase && !isUpperCase(term)) {
|
||||
return null; // no need for camel-case matcher for lower case query
|
||||
}
|
||||
var pattern = "";
|
||||
var upperCase = [];
|
||||
term.trim().split(/\s+/).forEach(function(w, index, array) {
|
||||
var tokens = w.split(/(?=[A-Z,.()<>?[\/])/);
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var s = tokens[i];
|
||||
if (s === "") {
|
||||
continue;
|
||||
}
|
||||
pattern += $.ui.autocomplete.escapeRegex(s);
|
||||
isWordToken = /\w$/.test(s);
|
||||
// ',' and '?' are the only delimiters commonly followed by space in java signatures
|
||||
pattern += "(" + $.ui.autocomplete.escapeRegex(s).replace(/[,?]/g, "$&\\s*?") + ")";
|
||||
upperCase.push(false);
|
||||
var isWordToken = /\w$/.test(s);
|
||||
if (isWordToken) {
|
||||
pattern += "([a-z0-9_$<>\\[\\]]*?)";
|
||||
if (i === tokens.length - 1 && index < array.length - 1) {
|
||||
// space in query string matches all delimiters
|
||||
pattern += "(.*?)";
|
||||
upperCase.push(isUpperCase(s[0]));
|
||||
} else {
|
||||
if (!camelCase && isUpperCase(s) && s.length === 1) {
|
||||
pattern += "()";
|
||||
} else {
|
||||
pattern += "([a-z0-9$<>?[\\]]*?)";
|
||||
}
|
||||
upperCase.push(isUpperCase(s[0]));
|
||||
}
|
||||
} else {
|
||||
pattern += "()";
|
||||
upperCase.push(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
return pattern;
|
||||
var re = new RegExp(pattern, "gi");
|
||||
re.upperCase = upperCase;
|
||||
return re;
|
||||
}
|
||||
function createMatcher(pattern, flags) {
|
||||
var isCamelCase = /[A-Z]/.test(pattern);
|
||||
return new RegExp(pattern, flags + (isCamelCase ? "" : "i"));
|
||||
}
|
||||
$(function() {
|
||||
var search = $("#search-input");
|
||||
var reset = $("#reset-button");
|
||||
search.val('');
|
||||
search.prop("disabled", false);
|
||||
reset.prop("disabled", false);
|
||||
reset.click(function() {
|
||||
search.val('').focus();
|
||||
});
|
||||
search.focus();
|
||||
});
|
||||
$.widget("custom.catcomplete", $.ui.autocomplete, {
|
||||
_create: function() {
|
||||
this._super();
|
||||
this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
|
||||
},
|
||||
_renderMenu: function(ul, items) {
|
||||
var rMenu = this;
|
||||
var currentCategory = "";
|
||||
rMenu.menu.bindings = $();
|
||||
$.each(items, function(index, item) {
|
||||
var li;
|
||||
if (item.category && item.category !== currentCategory) {
|
||||
ul.append("<li class=\"ui-autocomplete-category\">" + item.category + "</li>");
|
||||
currentCategory = item.category;
|
||||
}
|
||||
li = rMenu._renderItemData(ul, item);
|
||||
if (item.category) {
|
||||
li.attr("aria-label", item.category + " : " + item.l);
|
||||
li.attr("class", "result-item");
|
||||
} else {
|
||||
li.attr("aria-label", item.l);
|
||||
li.attr("class", "result-item");
|
||||
}
|
||||
});
|
||||
},
|
||||
_renderItem: function(ul, item) {
|
||||
var label = "";
|
||||
var matcher = createMatcher(escapeHtml(searchPattern), "g");
|
||||
var fallbackMatcher = new RegExp(fallbackPattern, "gi")
|
||||
if (item.category === catModules) {
|
||||
label = getHighlightedText(item.l, matcher, fallbackMatcher);
|
||||
} else if (item.category === catPackages) {
|
||||
label = getHighlightedText(item.l, matcher, fallbackMatcher);
|
||||
} else if (item.category === catTypes) {
|
||||
label = (item.p && item.p !== UNNAMED)
|
||||
? getHighlightedText(item.p + "." + item.l, matcher, fallbackMatcher)
|
||||
: getHighlightedText(item.l, matcher, fallbackMatcher);
|
||||
} else if (item.category === catMembers) {
|
||||
label = (item.p && item.p !== UNNAMED)
|
||||
? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher, fallbackMatcher)
|
||||
: getHighlightedText(item.c + "." + item.l, matcher, fallbackMatcher);
|
||||
} else if (item.category === catSearchTags) {
|
||||
label = getHighlightedText(item.l, matcher, fallbackMatcher);
|
||||
} else {
|
||||
label = item.l;
|
||||
}
|
||||
var li = $("<li/>").appendTo(ul);
|
||||
var div = $("<div/>").appendTo(li);
|
||||
if (item.category === catSearchTags && item.h) {
|
||||
if (item.d) {
|
||||
div.html(label + "<span class=\"search-tag-holder-result\"> (" + item.h + ")</span><br><span class=\"search-tag-desc-result\">"
|
||||
+ item.d + "</span><br>");
|
||||
} else {
|
||||
div.html(label + "<span class=\"search-tag-holder-result\"> (" + item.h + ")</span>");
|
||||
}
|
||||
} else {
|
||||
if (item.m) {
|
||||
div.html(item.m + "/" + label);
|
||||
} else {
|
||||
div.html(label);
|
||||
}
|
||||
}
|
||||
return li;
|
||||
function analyzeMatch(matcher, input, startOfName, category) {
|
||||
var from = startOfName;
|
||||
matcher.lastIndex = from;
|
||||
var match = matcher.exec(input);
|
||||
while (!match && from > 1) {
|
||||
from = input.lastIndexOf(".", from - 2) + 1;
|
||||
matcher.lastIndex = from;
|
||||
match = matcher.exec(input);
|
||||
}
|
||||
});
|
||||
function rankMatch(match, category) {
|
||||
if (!match) {
|
||||
return NO_MATCH;
|
||||
}
|
||||
var index = match.index;
|
||||
var input = match.input;
|
||||
var leftBoundaryMatch = 2;
|
||||
var periferalMatch = 0;
|
||||
// make sure match is anchored on a left word boundary
|
||||
if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index]) {
|
||||
leftBoundaryMatch = 0;
|
||||
} else if ("_" === input[index - 1] || (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input))) {
|
||||
leftBoundaryMatch = 1;
|
||||
}
|
||||
var matchEnd = index + match[0].length;
|
||||
var boundaries = [];
|
||||
var matchEnd = match.index + match[0].length;
|
||||
var leftParen = input.indexOf("(");
|
||||
var endOfName = leftParen > -1 ? leftParen : input.length;
|
||||
// exclude peripheral matches
|
||||
if (category !== catModules && category !== catSearchTags) {
|
||||
var delim = category === catPackages ? "/" : ".";
|
||||
if (leftParen > -1 && leftParen < index) {
|
||||
periferalMatch += 2;
|
||||
} else if (input.lastIndexOf(delim, endOfName) >= matchEnd) {
|
||||
periferalMatch += 2;
|
||||
if (category !== "modules" && category !== "searchTags") {
|
||||
if (leftParen > -1 && leftParen < match.index) {
|
||||
return NO_MATCH;
|
||||
} else if (startOfName - 1 >= matchEnd) {
|
||||
return NO_MATCH;
|
||||
}
|
||||
}
|
||||
var delta = match[0].length === endOfName ? 0 : 1; // rank full match higher than partial match
|
||||
for (var i = 1; i < match.length; i++) {
|
||||
// lower ranking if parts of the name are missing
|
||||
if (match[i])
|
||||
delta += match[i].length;
|
||||
}
|
||||
if (category === catTypes) {
|
||||
// lower ranking if a type name contains unmatched camel-case parts
|
||||
if (/[A-Z]/.test(input.substring(matchEnd)))
|
||||
delta += 5;
|
||||
if (/[A-Z]/.test(input.substring(0, index)))
|
||||
delta += 5;
|
||||
}
|
||||
return leftBoundaryMatch + periferalMatch + (delta / 200);
|
||||
var endOfName = leftParen > -1 ? leftParen : input.length;
|
||||
var score = 5;
|
||||
var start = match.index;
|
||||
var prevEnd = -1;
|
||||
for (var i = 1; i < match.length; i += 2) {
|
||||
var isUpper = isUpperCase(input[start]);
|
||||
var isMatcherUpper = matcher.upperCase[i];
|
||||
// capturing groups come in pairs, match and non-match
|
||||
boundaries.push(start, start + match[i].length);
|
||||
// make sure groups are anchored on a left word boundary
|
||||
var prevChar = input[start - 1] || "";
|
||||
var nextChar = input[start + 1] || "";
|
||||
if (start !== 0 && !/[\W_]/.test(prevChar) && !/[\W_]/.test(input[start])) {
|
||||
if (isUpper && (isLowerCase(prevChar) || isLowerCase(nextChar))) {
|
||||
score -= 0.1;
|
||||
} else if (isMatcherUpper && start === prevEnd) {
|
||||
score -= isUpper ? 0.1 : 1.0;
|
||||
} else {
|
||||
return NO_MATCH;
|
||||
}
|
||||
}
|
||||
prevEnd = start + match[i].length;
|
||||
start += match[i].length + match[i + 1].length;
|
||||
|
||||
// lower score for parts of the name that are missing
|
||||
if (match[i + 1] && prevEnd < endOfName) {
|
||||
score -= rateNoise(match[i + 1]);
|
||||
}
|
||||
}
|
||||
// lower score if a type name contains unmatched camel-case parts
|
||||
if (input[matchEnd - 1] !== "." && endOfName > matchEnd)
|
||||
score -= rateNoise(input.slice(matchEnd, endOfName));
|
||||
score -= rateNoise(input.slice(0, Math.max(startOfName, match.index)));
|
||||
|
||||
if (score <= 0) {
|
||||
return NO_MATCH;
|
||||
}
|
||||
return {
|
||||
input: input,
|
||||
score: score,
|
||||
category: category,
|
||||
boundaries: boundaries
|
||||
};
|
||||
}
|
||||
function isUpperCase(s) {
|
||||
return s !== s.toLowerCase();
|
||||
}
|
||||
function isLowerCase(s) {
|
||||
return s !== s.toUpperCase();
|
||||
}
|
||||
function rateNoise(str) {
|
||||
return (str.match(/([.(])/g) || []).length / 5
|
||||
+ (str.match(/([A-Z]+)/g) || []).length / 10
|
||||
+ str.length / 20;
|
||||
}
|
||||
function doSearch(request, response) {
|
||||
var result = [];
|
||||
searchPattern = createSearchPattern(request.term);
|
||||
fallbackPattern = createSearchPattern(request.term.toLowerCase());
|
||||
if (searchPattern === "") {
|
||||
var term = request.term.trim();
|
||||
if (term.length === 0) {
|
||||
return this.close();
|
||||
}
|
||||
var camelCaseMatcher = createMatcher(searchPattern, "");
|
||||
var fallbackMatcher = new RegExp(fallbackPattern, "i");
|
||||
var matcher = {
|
||||
plainMatcher: createMatcher(term, false),
|
||||
camelCaseMatcher: createMatcher(term, true)
|
||||
}
|
||||
var indexLoaded = indexFilesLoaded();
|
||||
|
||||
function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) {
|
||||
if (indexArray) {
|
||||
var newResults = [];
|
||||
$.each(indexArray, function (i, item) {
|
||||
item.category = category;
|
||||
var ranking = rankMatch(matcher.exec(nameFunc(item)), category);
|
||||
if (ranking < RANKING_THRESHOLD) {
|
||||
newResults.push({ranking: ranking, item: item});
|
||||
function getPrefix(item, category) {
|
||||
switch (category) {
|
||||
case "packages":
|
||||
return checkUnnamed(item.m, "/");
|
||||
case "types":
|
||||
return checkUnnamed(item.p, ".");
|
||||
case "members":
|
||||
return checkUnnamed(item.p, ".") + item.c + ".";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
function useQualifiedName(category) {
|
||||
switch (category) {
|
||||
case "packages":
|
||||
return /[\s/]/.test(term);
|
||||
case "types":
|
||||
case "members":
|
||||
return /[\s.]/.test(term);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function searchIndex(indexArray, category) {
|
||||
var matches = [];
|
||||
if (!indexArray) {
|
||||
if (!indexLoaded) {
|
||||
matches.push({ l: messages.loading, category: category });
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
$.each(indexArray, function (i, item) {
|
||||
var prefix = getPrefix(item, category);
|
||||
var simpleName = item.l;
|
||||
var qualifiedName = prefix + simpleName;
|
||||
var useQualified = useQualifiedName(category);
|
||||
var input = useQualified ? qualifiedName : simpleName;
|
||||
var startOfName = useQualified ? prefix.length : 0;
|
||||
var m = analyzeMatch(matcher.plainMatcher, input, startOfName, category);
|
||||
if (m === NO_MATCH && matcher.camelCaseMatcher) {
|
||||
m = analyzeMatch(matcher.camelCaseMatcher, input, startOfName, category);
|
||||
}
|
||||
if (m !== NO_MATCH) {
|
||||
m.indexItem = item;
|
||||
m.prefix = prefix;
|
||||
if (!useQualified) {
|
||||
m.input = qualifiedName;
|
||||
m.boundaries = m.boundaries.map(function(b) {
|
||||
return b + prefix.length;
|
||||
});
|
||||
}
|
||||
return newResults.length <= MAX_RESULTS;
|
||||
});
|
||||
return newResults.sort(function(e1, e2) {
|
||||
return e1.ranking - e2.ranking;
|
||||
}).map(function(e) {
|
||||
return e.item;
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function searchIndex(indexArray, category, nameFunc) {
|
||||
var primaryResults = searchIndexWithMatcher(indexArray, camelCaseMatcher, category, nameFunc);
|
||||
result = result.concat(primaryResults);
|
||||
if (primaryResults.length <= MIN_RESULTS && !camelCaseMatcher.ignoreCase) {
|
||||
var secondaryResults = searchIndexWithMatcher(indexArray, fallbackMatcher, category, nameFunc);
|
||||
result = result.concat(secondaryResults.filter(function (item) {
|
||||
return primaryResults.indexOf(item) === -1;
|
||||
}));
|
||||
}
|
||||
matches.push(m);
|
||||
}
|
||||
return matches.length < MAX_RESULTS;
|
||||
});
|
||||
return matches.sort(function(e1, e2) {
|
||||
return e2.score - e1.score;
|
||||
});
|
||||
}
|
||||
|
||||
searchIndex(moduleSearchIndex, catModules, function(item) { return item.l; });
|
||||
searchIndex(packageSearchIndex, catPackages, function(item) {
|
||||
return (item.m && request.term.indexOf("/") > -1)
|
||||
? (item.m + "/" + item.l) : item.l;
|
||||
});
|
||||
searchIndex(typeSearchIndex, catTypes, function(item) {
|
||||
return request.term.indexOf(".") > -1 ? item.p + "." + item.l : item.l;
|
||||
});
|
||||
searchIndex(memberSearchIndex, catMembers, function(item) {
|
||||
return request.term.indexOf(".") > -1
|
||||
? item.p + "." + item.c + "." + item.l : item.l;
|
||||
});
|
||||
searchIndex(tagSearchIndex, catSearchTags, function(item) { return item.l; });
|
||||
var result = searchIndex(moduleSearchIndex, "modules")
|
||||
.concat(searchIndex(packageSearchIndex, "packages"))
|
||||
.concat(searchIndex(typeSearchIndex, "types"))
|
||||
.concat(searchIndex(memberSearchIndex, "members"))
|
||||
.concat(searchIndex(tagSearchIndex, "searchTags"));
|
||||
|
||||
if (!indexFilesLoaded()) {
|
||||
if (!indexLoaded) {
|
||||
updateSearchResults = function() {
|
||||
doSearch(request, response);
|
||||
}
|
||||
result.unshift(loading);
|
||||
} else {
|
||||
updateSearchResults = function() {};
|
||||
}
|
||||
response(result);
|
||||
}
|
||||
// JQuery search menu implementation
|
||||
$.widget("custom.catcomplete", $.ui.autocomplete, {
|
||||
_create: function() {
|
||||
this._super();
|
||||
this.widget().menu("option", "items", "> .result-item");
|
||||
},
|
||||
_renderMenu: function(ul, items) {
|
||||
var currentCategory = "";
|
||||
var widget = this;
|
||||
widget.menu.bindings = $();
|
||||
$.each(items, function(index, item) {
|
||||
if (item.category && item.category !== currentCategory) {
|
||||
ul.append("<li class='ui-autocomplete-category'>" + categories[item.category] + "</li>");
|
||||
currentCategory = item.category;
|
||||
}
|
||||
var li = widget._renderItemData(ul, item);
|
||||
if (item.category) {
|
||||
li.attr("aria-label", categories[item.category] + " : " + item.l);
|
||||
} else {
|
||||
li.attr("aria-label", item.l);
|
||||
}
|
||||
li.attr("class", "result-item");
|
||||
});
|
||||
},
|
||||
_renderItem: function(ul, item) {
|
||||
var li = $("<li/>").appendTo(ul);
|
||||
var div = $("<div/>").appendTo(li);
|
||||
var label = item.l
|
||||
? item.l
|
||||
: getHighlightedText(item.input, item.boundaries, 0, item.input.length);
|
||||
var idx = item.indexItem;
|
||||
if (item.category === "searchTags" && idx.h) {
|
||||
if (idx.d) {
|
||||
div.html(label + "<span class='search-tag-holder-result'> (" + idx.h + ")</span><br><span class='search-tag-desc-result'>"
|
||||
+ idx.d + "</span><br>");
|
||||
} else {
|
||||
div.html(label + "<span class='search-tag-holder-result'> (" + idx.h + ")</span>");
|
||||
}
|
||||
} else {
|
||||
div.html(label);
|
||||
}
|
||||
return li;
|
||||
}
|
||||
});
|
||||
$(function() {
|
||||
var expanded = false;
|
||||
var windowWidth;
|
||||
@ -308,13 +388,15 @@ $(function() {
|
||||
$(window).on("orientationchange", collapse).on("resize", function(e) {
|
||||
if (expanded && windowWidth !== window.innerWidth) collapse();
|
||||
});
|
||||
$("#search-input").catcomplete({
|
||||
var search = $("#search-input");
|
||||
var reset = $("#reset-button");
|
||||
search.catcomplete({
|
||||
minLength: 1,
|
||||
delay: 300,
|
||||
delay: 200,
|
||||
source: doSearch,
|
||||
response: function(event, ui) {
|
||||
if (!ui.content.length) {
|
||||
ui.content.push(noResult);
|
||||
ui.content.push({ l: messages.noResult });
|
||||
} else {
|
||||
$("#search-input").empty();
|
||||
}
|
||||
@ -327,41 +409,18 @@ $(function() {
|
||||
collision: "flip"
|
||||
},
|
||||
select: function(event, ui) {
|
||||
if (ui.item.category) {
|
||||
var url = getURLPrefix(ui);
|
||||
if (ui.item.category === catModules) {
|
||||
url += "module-summary.html";
|
||||
} else if (ui.item.category === catPackages) {
|
||||
if (ui.item.u) {
|
||||
url = ui.item.u;
|
||||
} else {
|
||||
url += ui.item.l.replace(/\./g, '/') + "/package-summary.html";
|
||||
}
|
||||
} else if (ui.item.category === catTypes) {
|
||||
if (ui.item.u) {
|
||||
url = ui.item.u;
|
||||
} else if (ui.item.p === UNNAMED) {
|
||||
url += ui.item.l + ".html";
|
||||
} else {
|
||||
url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.l + ".html";
|
||||
}
|
||||
} else if (ui.item.category === catMembers) {
|
||||
if (ui.item.p === UNNAMED) {
|
||||
url += ui.item.c + ".html" + "#";
|
||||
} else {
|
||||
url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.c + ".html" + "#";
|
||||
}
|
||||
if (ui.item.u) {
|
||||
url += ui.item.u;
|
||||
} else {
|
||||
url += ui.item.l;
|
||||
}
|
||||
} else if (ui.item.category === catSearchTags) {
|
||||
url += ui.item.u;
|
||||
}
|
||||
if (ui.item.indexItem) {
|
||||
var url = getURL(ui.item.indexItem, ui.item.category);
|
||||
window.location.href = pathtoroot + url;
|
||||
$("#search-input").focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
search.val('');
|
||||
search.prop("disabled", false);
|
||||
reset.prop("disabled", false);
|
||||
reset.click(function() {
|
||||
search.val('').focus();
|
||||
});
|
||||
search.focus();
|
||||
});
|
||||
|
||||
@ -324,10 +324,11 @@ doclet.help.annotation_interface.description=\
|
||||
doclet.help.search.head=Search
|
||||
# Introduction to Javadoc search features, followed by a list of examples
|
||||
doclet.help.search.intro=You can search for definitions of modules, packages, types, fields, methods, \
|
||||
system properties and other terms defined in the API, using some or all of the name, optionally \
|
||||
using "camelCase" abbreviations. For example:
|
||||
system properties and other terms defined in the API. These items can be searched using part or all \
|
||||
of the name, optionally using "camelCase" abbreviations, or multiple search terms separated by whitespace. \
|
||||
Some examples:
|
||||
# Used to list search examples, {0} is a search term and {1} the matching result
|
||||
doclet.help.search.example={0} will match {1}
|
||||
doclet.help.search.example={0} matches {1}
|
||||
# {0} contains a link to the current Javadoc Search Specification
|
||||
doclet.help.search.refer=Refer to the {0} for a full description of search features.
|
||||
# The URL for the Javadoc Search Specification. {0} will be replaced by the JDK version number
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -709,29 +709,9 @@ public class TestSearch extends JavadocTester {
|
||||
"##REPLACE:");
|
||||
|
||||
checkOutput("search.js", true,
|
||||
"function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) {",
|
||||
"""
|
||||
function getURLPrefix(ui) {
|
||||
var urlPrefix="";
|
||||
var slash = "/";
|
||||
if (ui.item.category === catModules) {
|
||||
return ui.item.l + slash;
|
||||
} else if (ui.item.category === catPackages && ui.item.m) {
|
||||
return ui.item.m + slash;
|
||||
} else if (ui.item.category === catTypes || ui.item.category === catMembers) {
|
||||
if (ui.item.m) {
|
||||
urlPrefix = ui.item.m + slash;
|
||||
} else {
|
||||
$.each(packageSearchIndex, function(index, item) {
|
||||
if (item.m && ui.item.p === item.l) {
|
||||
urlPrefix = item.m + slash;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return urlPrefix;
|
||||
}""",
|
||||
"url += ui.item.l;");
|
||||
"function searchIndex(indexArray, category) {",
|
||||
"function getURLPrefix(item, category) {",
|
||||
"url += item.l;");
|
||||
|
||||
checkCssClasses("search.js", "stylesheet.css");
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -109,7 +109,7 @@ public class TestSearchScript extends JavadocTester {
|
||||
"mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)", "mappkg.system.property"));
|
||||
checkSearch(inv, "MAP", List.of("mapmodule", "mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.Map", "mappkg.impl.MyMap",
|
||||
"mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)", "mappkg.system.property"));
|
||||
checkSearch(inv, "value", List.of("mappkg.impl.MyMap.OTHER_VALUE", "mappkg.impl.MyMap.some_value"));
|
||||
checkSearch(inv, "value", List.of("mappkg.impl.MyMap.some_value", "mappkg.impl.MyMap.OTHER_VALUE"));
|
||||
checkSearch(inv, "VALUE", List.of("mappkg.impl.MyMap.OTHER_VALUE", "mappkg.impl.MyMap.some_value"));
|
||||
checkSearch(inv, "map.other", List.of("mappkg.impl.MyMap.OTHER_VALUE"));
|
||||
checkSearch(inv, "Map.Some_", List.of("mappkg.impl.MyMap.some_value"));
|
||||
@ -167,13 +167,20 @@ public class TestSearchScript extends JavadocTester {
|
||||
// search tag
|
||||
checkSearch(inv, "search tag", List.of("search tag", "multiline search tag"));
|
||||
checkSearch(inv, "search tag", List.of("search tag", "multiline search tag"));
|
||||
checkSearch(inv, "search ", List.of("multiline search tag", "search tag"));
|
||||
checkSearch(inv, "tag", List.of("multiline search tag", "search tag"));
|
||||
checkSearch(inv, "sea", List.of("multiline search tag", "search tag"));
|
||||
checkSearch(inv, "search ", List.of("search tag", "multiline search tag"));
|
||||
checkSearch(inv, "tag", List.of("search tag", "multiline search tag"));
|
||||
checkSearch(inv, "sea", List.of("search tag", "multiline search tag"));
|
||||
checkSearch(inv, "multi", List.of("multiline search tag"));
|
||||
checkSearch(inv, "ear", List.of());
|
||||
}
|
||||
|
||||
// multiple space separated tokens
|
||||
checkSearch(inv, "my map map", List.of("mappkg.impl.MyMap.MyMap(Map)", "mappkg.impl.MyMap.MyMap()"));
|
||||
checkSearch(inv, "map get", List.of("mappkg.Map.get(Object)", "mappkg.impl.MyMap.get(Object)"));
|
||||
checkSearch(inv, "get", List.of("mappkg.impl.MyMap.get(Object)", "mappkg.Map.get(Object)"));
|
||||
checkSearch(inv, "get o", List.of("mappkg.Map.get(Object)", "mappkg.impl.MyMap.get(Object)"));
|
||||
checkSearch(inv, "put o o", List.of("mappkg.Map.put(Object, Object)", "mappkg.impl.MyMap.put(Object, Object)"));
|
||||
checkSearch(inv, "put(o o)", List.of("mappkg.Map.put(Object, Object)", "mappkg.impl.MyMap.put(Object, Object)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageSource() throws ScriptException, IOException, NoSuchMethodException {
|
||||
@ -187,16 +194,16 @@ public class TestSearchScript extends JavadocTester {
|
||||
Invocable inv = getEngine();
|
||||
|
||||
// exact match, case sensitvity, left boundaries
|
||||
checkSearch(inv, "list", List.of("listpkg", "listpkg.List", "listpkg.ListProvider", "listpkg.MyList",
|
||||
"listpkg.MyListFactory", "listpkg.ListProvider.ListProvider()",
|
||||
checkSearch(inv, "list", List.of("listpkg", "listpkg.List", "listpkg.MyList", "listpkg.ListProvider",
|
||||
"listpkg.MyListFactory", "listpkg.MyList.MyList()",
|
||||
"listpkg.MyListFactory.createList(ListProvider, MyListFactory)",
|
||||
"listpkg.ListProvider.makeNewList()",
|
||||
"listpkg.MyList.MyList()", "listpkg.MyListFactory.MyListFactory()"));
|
||||
checkSearch(inv, "List", List.of("listpkg", "listpkg.List", "listpkg.ListProvider", "listpkg.MyList",
|
||||
"listpkg.MyListFactory", "listpkg.ListProvider.ListProvider()",
|
||||
"listpkg.ListProvider.ListProvider()", "listpkg.ListProvider.makeNewList()",
|
||||
"listpkg.MyListFactory.MyListFactory()"));
|
||||
checkSearch(inv, "List", List.of("listpkg", "listpkg.List", "listpkg.MyList", "listpkg.ListProvider",
|
||||
"listpkg.MyListFactory", "listpkg.MyList.MyList()",
|
||||
"listpkg.MyListFactory.createList(ListProvider, MyListFactory)",
|
||||
"listpkg.ListProvider.makeNewList()",
|
||||
"listpkg.MyList.MyList()", "listpkg.MyListFactory.MyListFactory()"));
|
||||
"listpkg.ListProvider.ListProvider()", "listpkg.ListProvider.makeNewList()",
|
||||
"listpkg.MyListFactory.MyListFactory()"));
|
||||
// partial match
|
||||
checkSearch(inv, "fact", List.of("listpkg.MyListFactory", "listpkg.MyListFactory.MyListFactory()"));
|
||||
checkSearch(inv, "pro", List.of("listpkg.ListProvider", "listpkg.ListProvider.ListProvider()"));
|
||||
@ -278,9 +285,9 @@ public class TestSearchScript extends JavadocTester {
|
||||
checkSearch(inv, "some", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "SOME", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "Some", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "int", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "INT", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "Int", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "int", List.of("All Classes and Interfaces", "listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "INT", List.of("All Classes and Interfaces", "listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "Int", List.of("All Classes and Interfaces", "listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "int_con", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "INT_CON", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
checkSearch(inv, "NT", List.of());
|
||||
@ -289,9 +296,9 @@ public class TestSearchScript extends JavadocTester {
|
||||
checkSearch(inv, "_CONST", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
|
||||
|
||||
// Test for all packages, all classes links
|
||||
checkSearch(inv, "all", List.of("All Packages", "All Classes"));
|
||||
checkSearch(inv, "All", List.of("All Packages", "All Classes"));
|
||||
checkSearch(inv, "ALL", List.of("All Packages", "All Classes"));
|
||||
checkSearch(inv, "all", List.of("All Packages", "All Classes and Interfaces"));
|
||||
checkSearch(inv, "All", List.of("All Packages", "All Classes and Interfaces"));
|
||||
checkSearch(inv, "ALL", List.of("All Packages", "All Classes and Interfaces"));
|
||||
|
||||
// test for generic types, var-arg and array args
|
||||
checkSearch(inv, "(map<string, ? ext collection>)",
|
||||
@ -303,14 +310,36 @@ public class TestSearchScript extends JavadocTester {
|
||||
checkSearch(inv, "(e..", List.of("listpkg.List.of(E...)"));
|
||||
checkSearch(inv, "(int[]", List.of("listpkg.Nolist.withArrayArg(int[])"));
|
||||
checkSearch(inv, "(i[]", List.of("listpkg.Nolist.withArrayArg(int[])"));
|
||||
|
||||
// multiple space separated tokens
|
||||
checkSearch(inv, "list of", List.of("listpkg.List.of()", "listpkg.List.of(E)",
|
||||
"listpkg.List.of(E, E)", "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
|
||||
"listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
|
||||
checkSearch(inv, "list of e", List.of("listpkg.List.of(E)", "listpkg.List.of(E, E)",
|
||||
"listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
|
||||
"listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
|
||||
checkSearch(inv, "list of e e e", List.of("listpkg.List.of(E, E, E)",
|
||||
"listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)"));
|
||||
checkSearch(inv, "list of e..", List.of("listpkg.List.of(E...)"));
|
||||
|
||||
checkSearch(inv, "list with", List.of());
|
||||
checkSearch(inv, "nolist with", List.of("listpkg.Nolist.withVarArgs(Object...)",
|
||||
"listpkg.Nolist.withArrayArg(int[])",
|
||||
"listpkg.Nolist.withTypeParams(Map<String, ? extends Collection>)"));
|
||||
checkSearch(inv, "with arg", List.of("listpkg.Nolist.withVarArgs(Object...)",
|
||||
"listpkg.Nolist.withArrayArg(int[])"));
|
||||
checkSearch(inv, "with int", List.of("listpkg.Nolist.withArrayArg(int[])"));
|
||||
checkSearch(inv, "with map", List.of(
|
||||
"listpkg.Nolist.withTypeParams(Map<String, ? extends Collection>)"));
|
||||
|
||||
}
|
||||
|
||||
void checkSearch(Invocable inv, String query, List<String> results) throws ScriptException, NoSuchMethodException {
|
||||
checkList((List) inv.invokeFunction("search", query), results);
|
||||
checkList(query, (List<?>) inv.invokeFunction("search", query), results);
|
||||
}
|
||||
|
||||
void checkList(List<?> result, List<?> expected) {
|
||||
checking("Checking list: " + result);
|
||||
void checkList(String query, List<?> result, List<?> expected) {
|
||||
checking("Checking resut for query \"" + query + "\"");
|
||||
if (!expected.equals(result)) {
|
||||
failed("Expected: " + expected + ", got: " + result);
|
||||
} else {
|
||||
@ -320,7 +349,7 @@ public class TestSearchScript extends JavadocTester {
|
||||
|
||||
void checkNullSearch(Invocable inv, String query) throws ScriptException, NoSuchMethodException {
|
||||
Object result = inv.invokeFunction("search", query);
|
||||
checking("Checking null result");
|
||||
checking("Checking result for query \"" + query + "\"");
|
||||
if (result == null) {
|
||||
passed("Result is null as expected");
|
||||
} else {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -135,13 +135,16 @@ var console = {
|
||||
}
|
||||
};
|
||||
|
||||
var window = {
|
||||
innerWidth: 800
|
||||
}
|
||||
|
||||
var renderMenu = function(items) {
|
||||
var result = new java.util.ArrayList();
|
||||
var currentCategory = "";
|
||||
$.each(items, function(index, item) {
|
||||
var li;
|
||||
if (item.l !== noResult.l && item.category !== currentCategory) {
|
||||
// print(item.category);
|
||||
if (item.l !== messages.noResult && item.category !== currentCategory) {
|
||||
currentCategory = item.category;
|
||||
}
|
||||
result.add(renderItem(item));
|
||||
@ -150,25 +153,7 @@ var renderMenu = function(items) {
|
||||
};
|
||||
|
||||
var renderItem = function(item) {
|
||||
var label;
|
||||
if (item.category === catModules) {
|
||||
label = item.l;
|
||||
} else if (item.category === catPackages) {
|
||||
label = (item.m)
|
||||
? item.m + "/" + item.l
|
||||
: item.l;
|
||||
} else if (item.category === catTypes) {
|
||||
label = (item.p)
|
||||
? item.p + "." + item.l
|
||||
: item.l;
|
||||
} else if (item.category === catMembers) {
|
||||
label = item.p + "." + item.c + "." + item.l;
|
||||
} else if (item.category === catSearchTags) {
|
||||
label = item.l;
|
||||
} else {
|
||||
label = item.l;
|
||||
}
|
||||
return label;
|
||||
return item.l || item.input;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -143,11 +143,11 @@ public class TestTerminology extends JavadocTester {
|
||||
|
||||
checkOutput("search.js", sv.compareTo(SourceVersion.RELEASE_16) < 0,
|
||||
"""
|
||||
var catTypes = "Types";""" //
|
||||
types: "Types","""
|
||||
);
|
||||
checkOutput("search.js", sv.compareTo(SourceVersion.RELEASE_16) >= 0,
|
||||
"""
|
||||
var catTypes = "Classes and Interfaces";"""
|
||||
types: "Classes and Interfaces","""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user