diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index e2b4cc50dd580..58898247a9f40 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -328,10 +328,11 @@ details:not(.toggle) summary {
margin-bottom: .6em;
}
-code, pre, a.test-arrow, .code-header {
+code, pre, a.test-arrow, .code-header, .search-results .type-signature {
font-family: "Source Code Pro", monospace;
}
-.docblock code, .docblock-short code {
+.docblock code, .docblock-short code,
+.search-results .type-signature strong {
border-radius: 3px;
padding: 0 0.125em;
}
@@ -681,7 +682,8 @@ ul.block, .block li {
}
.docblock code, .docblock-short code,
-pre, .rustdoc.src .example-wrap {
+pre, .rustdoc.src .example-wrap,
+.search-results .type-signature strong {
background-color: var(--code-block-background-color);
}
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 48c9a53a28310..a2878be2817df 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -262,6 +262,14 @@ function initSearch(rawSearchIndex) {
* Special type name IDs for searching by both array and slice (`[]` syntax).
*/
let typeNameIdOfArrayOrSlice;
+ /**
+ * Special type name IDs for searching never (the `!` type).
+ */
+ let typeNameIdOfNever;
+ /**
+ * Special type name IDs for searching tuple (the `()` type).
+ */
+ let typeNameIdOfTuple;
/**
* Add an item to the type Name->ID map, or, if one already exists, use it.
@@ -467,12 +475,13 @@ function initSearch(rawSearchIndex) {
* @param {ParsedQuery} query
* @param {ParserState} parserState
* @param {string} name - Name of the query element.
+ * @param {string} normalizedName - Name of the query element will be used for searching.
* @param {Array} generics - List of generics of this query element.
*
* @return {QueryElement} - The newly created `QueryElement`.
*/
- function createQueryElement(query, parserState, name, generics, isInGenerics) {
- const path = name.trim();
+ function createQueryElement(query, parserState, name, normalizedName, generics, isInGenerics) {
+ const path = normalizedName.trim();
if (path.length === 0 && generics.length === 0) {
throw ["Unexpected ", parserState.userQuery[parserState.pos]];
} else if (path === "*") {
@@ -483,7 +492,7 @@ function initSearch(rawSearchIndex) {
}
const typeFilter = parserState.typeFilter;
parserState.typeFilter = null;
- if (name === "!") {
+ if (normalizedName === "!") {
if (typeFilter !== null && typeFilter !== "primitive") {
throw [
"Invalid search type: primitive never type ",
@@ -502,6 +511,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: "never",
+ normalizedName: "never",
id: null,
fullPath: ["never"],
pathWithoutLast: [],
@@ -544,6 +554,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: name.trim(),
+ normalizedName: normalizedName.trim(),
id: null,
fullPath: pathSegments,
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
@@ -673,6 +684,7 @@ function initSearch(rawSearchIndex) {
}
elems.push({
name: "[]",
+ normalizedName: "[]",
id: null,
fullPath: ["[]"],
pathWithoutLast: [],
@@ -709,6 +721,7 @@ function initSearch(rawSearchIndex) {
createQueryElement(
query,
parserState,
+ parserState.original.slice(start, end),
parserState.userQuery.slice(start, end),
generics,
isInGenerics
@@ -771,7 +784,7 @@ function initSearch(rawSearchIndex) {
// The type filter doesn't count as an element since it's a modifier.
const typeFilterElem = elems.pop();
checkExtraTypeFilterCharacters(start, parserState);
- parserState.typeFilter = typeFilterElem.name;
+ parserState.typeFilter = typeFilterElem.normalizedName;
parserState.pos += 1;
parserState.totalElems -= 1;
query.literalSearch = false;
@@ -894,7 +907,7 @@ function initSearch(rawSearchIndex) {
// The type filter doesn't count as an element since it's a modifier.
const typeFilterElem = query.elems.pop();
checkExtraTypeFilterCharacters(start, parserState);
- parserState.typeFilter = typeFilterElem.name;
+ parserState.typeFilter = typeFilterElem.normalizedName;
parserState.pos += 1;
parserState.totalElems -= 1;
query.literalSearch = false;
@@ -1063,6 +1076,7 @@ function initSearch(rawSearchIndex) {
totalElems: 0,
genericsElems: 0,
typeFilter: null,
+ original: userQuery,
userQuery: userQuery.toLowerCase(),
};
let query = newParsedQuery(userQuery);
@@ -1129,29 +1143,35 @@ function initSearch(rawSearchIndex) {
* marked for removal.
*
* @param {[ResultObject]} results
+ * @param {boolean} isType - True will include function signature string
* @returns {[ResultObject]}
*/
- function transformResults(results) {
+ function transformResults(results, isType) {
const duplicates = new Set();
const out = [];
for (const result of results) {
if (result.id !== -1) {
const obj = searchIndex[result.id];
- obj.dist = result.dist;
const res = buildHrefAndPath(obj);
- obj.displayPath = pathSplitter(res[0]);
- obj.fullPath = obj.displayPath + obj.name;
+ const displayPath = pathSplitter(res[0]);
+ let fullPath = displayPath + obj.name;
// To be sure than it some items aren't considered as duplicate.
- obj.fullPath += "|" + obj.ty;
+ fullPath += "|" + obj.ty;
- if (duplicates.has(obj.fullPath)) {
+ if (!isType && duplicates.has(fullPath)) {
continue;
}
- duplicates.add(obj.fullPath);
-
- obj.href = res[1];
- out.push(obj);
+ duplicates.add(fullPath);
+
+ out.push(Object.assign({
+ dist: result.dist,
+ index: result.index,
+ displayTypeSignature: result.displayTypeSignature,
+ displayPath,
+ fullPath,
+ href: res[1],
+ }, obj));
if (out.length >= MAX_RESULTS) {
break;
}
@@ -1160,6 +1180,119 @@ function initSearch(rawSearchIndex) {
return out;
}
+ /**
+ * Convert a type signature to a readable string.
+ *
+ * @param {FunctionSearchType?} results
+ *
+ * @returns string?
+ */
+ function formatResultTypeSignature(type) {
+ if (type === null) {
+ return null;
+ }
+
+ let result = [];
+
+ function pushNotHighlighted(text) {
+ if (result.length % 2 === 0) {
+ result.push("");
+ }
+ result[result.length - 1] = `${result[result.length - 1]}${text}`;
+ }
+ function pushHighlighted(text) {
+ if (result.length === 0) {
+ result.push("");
+ }
+ if (result.length % 2 !== 0) {
+ result.push("");
+ }
+ result[result.length - 1] = `${result[result.length - 1]}${text}`;
+ }
+ function push(text, highlighted) {
+ if (highlighted) {
+ pushHighlighted(text);
+ } else {
+ pushNotHighlighted(text);
+ }
+ }
+
+ function isTransitivelyHighlighted(fnType) {
+ // Caching the flag is necessary to avoid quadratic complexity
+ // in this function.
+ if (Object.prototype.hasOwnProperty.call(fnType, "isTransitivelyHighlighted")) {
+ return fnType.isTransitivelyHighlighted;
+ }
+ const isHighlighted = fnType.highlighted ||
+ (fnType.generics && fnType.generics.length > 0 &&
+ fnType.generics.some(isTransitivelyHighlighted)) ||
+ (type.where_clause && type.where_clause[(-fnType.id) - 1] &&
+ type.where_clause[(-fnType.id) - 1].some(isTransitivelyHighlighted));
+ fnType.isTransitivelyHighlighted = isHighlighted;
+ return isHighlighted;
+ }
+
+ function formatTypeList(fnTypes, delim) {
+ if (!fnTypes) {
+ return;
+ }
+ let first = true;
+ for (const fnType of fnTypes) {
+ if (first) {
+ first = false;
+ } else {
+ pushNotHighlighted(delim);
+ }
+ if (fnType.ty === TY_PRIMITIVE &&
+ (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+ ) {
+ push("[", fnType.highlighted);
+ if (fnType.generics && fnType.generics.some(isTransitivelyHighlighted)) {
+ formatTypeList(fnType.generics, ", ");
+ }
+ push("]", fnType.highlighted);
+ } else if (fnType.ty === TY_PRIMITIVE && fnType.id === typeNameIdOfNever) {
+ push("!", fnType.highlighted);
+ } else if (fnType.name === null) {
+ if (fnType.generics && fnType.generics.some(t => t.name !== null)) {
+ push("impl ", fnType.highlighted);
+ formatTypeList(fnType.generics, " + ");
+ } else if (type.where_clause &&
+ type.where_clause[(-fnType.id) - 1].some(isTransitivelyHighlighted)
+ ) {
+ push("impl ", fnType.highlighted);
+ formatTypeList(type.where_clause[(-fnType.id) - 1], " + ");
+ } else {
+ push("_", fnType.highlighted);
+ }
+ } else {
+ push(fnType.name, fnType.highlighted);
+ if (fnType.generics && fnType.generics.some(isTransitivelyHighlighted)) {
+ pushNotHighlighted("<");
+ formatTypeList(fnType.generics, ", ");
+ pushNotHighlighted(">");
+ }
+ }
+ }
+ }
+
+ formatTypeList(type.inputs, ", ");
+ if (result.every(f => f === "")) {
+ result = [];
+ }
+ const returnsUnit = type.output.length === 1 &&
+ type.output[0].id === typeNameIdOfTuple &&
+ type.output[0].generics.length === 0;
+ if (type.output.length !== 0 && !returnsUnit) {
+ pushNotHighlighted(result.length === 0 ? "-> " : " -> ");
+ formatTypeList(type.output, ", ");
+ }
+ if (result.every(f => f === "")) {
+ return null;
+ }
+ return result;
+ }
+
/**
* This function takes a result map, and sorts it by various criteria, including edit
* distance, substring match, and the crate it comes from.
@@ -1305,39 +1438,37 @@ function initSearch(rawSearchIndex) {
result.id = -1;
}
}
- return transformResults(result_list);
+ return transformResults(result_list, isType);
}
/**
- * This function checks generics in search query `queryElem` can all be found in the
- * search index (`fnType`),
- *
- * This function returns `true` if it matches, and also writes the results to mgensInout.
- * It returns `false` if no match is found, and leaves mgensInout untouched.
+ * The type tree on each function is annotated with information about which
+ * node to highlight. This is then used to generate the formatted type
+ * signature for the result.
*
- * @param {FunctionType} fnType - The object to check.
- * @param {QueryElement} queryElem - The element from the parsed query.
- * @param {[FunctionType]} whereClause - Trait bounds for generic items.
- * @param {Map|null} mgensInout - Map functions generics to query generics.
- *
- * @return {boolean} - Returns true if a match, false otherwise.
+ * Between each result, it gets cleared out. This function clears it.
*/
- function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
- return unifyFunctionTypes(
- fnType.generics,
- queryElem.generics,
- whereClause,
- mgensInout,
- mgens => {
- if (mgensInout) {
- for (const [fid, qid] of mgens.entries()) {
- mgensInout.set(fid, qid);
- }
- }
- return true;
+ function clearHighlighted(fnType) {
+ function doClearHighlighted(fnTypeList) {
+ if (!fnTypeList) {
+ return;
}
- );
+ for (const fnType of fnTypeList) {
+ fnType.highlighted = false;
+ delete fnType.isTransitivelyHighlighted;
+ delete fnType.matchedGenerics;
+ doClearHighlighted(fnType.generics);
+ }
+ }
+ doClearHighlighted(fnType.inputs);
+ doClearHighlighted(fnType.output);
+ if (fnType.where_clause) {
+ for (const where of fnType.where_clause) {
+ doClearHighlighted(where);
+ }
+ }
}
+
/**
* This function checks if a list of search query `queryElems` can all be found in the
* search index (`fnTypes`).
@@ -1353,9 +1484,10 @@ function initSearch(rawSearchIndex) {
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
* @param {Map|null} mgensIn
* - Map functions generics to query generics (never modified).
- * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution.
+ * @param {(null|Map, Array) -> bool} solutionCb
+ * - Called for each `mgens` solution.
*
- * @return {boolean} - Returns true if a match, false otherwise.
+ * @return {Array|false} - Returns array of matched parts, false otherwise.
*/
function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
/**
@@ -1363,7 +1495,7 @@ function initSearch(rawSearchIndex) {
*/
let mgens = new Map(mgensIn);
if (queryElems.length === 0) {
- return !solutionCb || solutionCb(mgens);
+ return !solutionCb || solutionCb(mgens, []);
}
if (!fnTypesIn || fnTypesIn.length === 0) {
return false;
@@ -1411,6 +1543,7 @@ function initSearch(rawSearchIndex) {
queryElemsOffset,
fnTypesOffset,
unbox,
+ matchedGenerics,
} = backtracking.pop();
mgens = new Map(mgensScratch);
const fnType = fnTypesScratch[fnTypesOffset];
@@ -1440,6 +1573,7 @@ function initSearch(rawSearchIndex) {
fl = fnTypes.length;
const tmp = fnTypes[queryElemsOffset];
fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+ fnTypes[queryElemsOffset].matchedGenerics = matchedGenerics;
fnTypes[fnTypesOffset] = tmp;
// this is known as a good match; go to the next one
i = queryElemsOffset;
@@ -1470,10 +1604,11 @@ function initSearch(rawSearchIndex) {
queryElem.generics,
whereClause,
mgens,
- mgensScratch => {
+ (mgensScratch, matchedGenerics) => {
matchCandidates.push({
fnTypesScratch,
mgensScratch,
+ matchedGenerics,
queryElemsOffset: i,
fnTypesOffset: j,
unbox: false,
@@ -1492,6 +1627,7 @@ function initSearch(rawSearchIndex) {
backtracking.push({
fnTypesScratch,
mgensScratch,
+ matchedGenerics: [],
queryElemsOffset: i,
fnTypesOffset: j,
unbox: true,
@@ -1506,7 +1642,8 @@ function initSearch(rawSearchIndex) {
}
}
// use the current candidate
- const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+ const {fnTypesOffset: candidate, mgensScratch: mgensNew, matchedGenerics}
+ = matchCandidates.shift();
if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
mgens.set(fnTypes[candidate].id, queryElems[i].id);
}
@@ -1523,9 +1660,22 @@ function initSearch(rawSearchIndex) {
for (const otherCandidate of matchCandidates) {
backtracking.push(otherCandidate);
}
+ fnTypes[i].matchedGenerics = matchedGenerics;
// If we're on the last item, check the solution with the callback
// backtrack if the callback says its unsuitable
- while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+ while (i === (ql - 1) && solutionCb) {
+ // The above loop partitions the list into "matched" and "unmatched" parts
+ // by swapping things into place when they match.
+ let highlightable = fnTypes.slice(0, ql);
+ for (let j = 0; j < highlightable.length; ++j) {
+ if (highlightable[j].matchedGenerics.length === 0) {
+ continue;
+ }
+ highlightable = [...highlightable, ...highlightable[j].matchedGenerics];
+ }
+ if (solutionCb(mgens, highlightable)) {
+ break;
+ }
if (!backtrack()) {
return false;
}
@@ -1649,38 +1799,15 @@ function initSearch(rawSearchIndex) {
* @return {boolean} - Returns true if the type matches, false otherwise.
*/
function checkType(row, elem, whereClause) {
- if (row.id === null) {
- // This is a pure "generic" search, no need to run other checks.
- return row.generics.length > 0
- ? checkIfInList(row.generics, elem, whereClause)
- : false;
- }
-
- if (row.id < 0 && elem.id >= 0) {
- const gid = (-row.id) - 1;
- return checkIfInList(whereClause[gid], elem, whereClause);
- }
-
- if (row.id < 0 && elem.id < 0) {
- return true;
- }
-
- const matchesExact = row.id === elem.id;
- const matchesArrayOrSlice = elem.id === typeNameIdOfArrayOrSlice &&
- (row.id === typeNameIdOfSlice || row.id === typeNameIdOfArray);
-
- if ((matchesExact || matchesArrayOrSlice) &&
- typePassesFilter(elem.typeFilter, row.ty)) {
- if (elem.generics.length > 0) {
- return checkGenerics(row, elem, whereClause, new Map());
- }
- return true;
+ if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
+ row.generics.length === 0 && elem.generics.length === 0 &&
+ // special case
+ elem.id !== typeNameIdOfArrayOrSlice
+ ) {
+ return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty);
+ } else {
+ return unifyFunctionTypes([row], [elem], whereClause);
}
-
- // If the current item does not match, try [unboxing] the generic.
- // [unboxing]:
- // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- return checkIfInList(row.generics, elem, whereClause);
}
function checkPath(contains, ty, maxEditDistance) {
@@ -1831,8 +1958,19 @@ function initSearch(rawSearchIndex) {
* @param {integer} index
* @param {integer} dist
* @param {integer} path_dist
+ * @param {integer} maxEditDistance
+ * @param {boolean} isType
*/
- function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) {
+ function addIntoResults(
+ results,
+ fullId,
+ id,
+ index,
+ dist,
+ path_dist,
+ maxEditDistance,
+ isType
+ ) {
const inBounds = dist <= maxEditDistance || index !== -1;
if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) {
if (results.has(fullId)) {
@@ -1841,12 +1979,16 @@ function initSearch(rawSearchIndex) {
return;
}
}
+ const displayTypeSignature = isType ?
+ formatResultTypeSignature(searchIndex[id].type) :
+ null;
results.set(fullId, {
id: id,
index: index,
dontValidate: parsedQuery.literalSearch,
dist: dist,
path_dist: path_dist,
+ displayTypeSignature,
});
}
}
@@ -1885,14 +2027,46 @@ function initSearch(rawSearchIndex) {
const in_args = row.type && row.type.inputs
&& checkIfInList(row.type.inputs, elem, row.type.where_clause);
if (in_args) {
+ // Generate type signature information for search result display.
+ clearHighlighted(row.type);
+ unifyFunctionTypes(
+ row.type.inputs,
+ parsedQuery.elems,
+ row.type.where_clause,
+ null,
+ (mgens, parts) => {
+ writeNamesToTypeParameters(mgens, row.type, parsedQuery);
+ for (const part of parts) {
+ part.highlighted = true;
+ }
+ return true;
+ }
+ );
// path_dist is 0 because no parent path information is currently stored
// in the search index
- addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
+ addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance, true);
}
const returned = row.type && row.type.output
&& checkIfInList(row.type.output, elem, row.type.where_clause);
if (returned) {
- addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
+ // Generate type signature information for search result display.
+ if (!in_args) {
+ clearHighlighted(row.type);
+ unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.elems,
+ row.type.where_clause,
+ null,
+ (mgens, parts) => {
+ writeNamesToTypeParameters(mgens, row.type, parsedQuery);
+ for (const part of parts) {
+ part.highlighted = true;
+ }
+ return true;
+ }
+ );
+ }
+ addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance, true);
}
if (!typePassesFilter(elem.typeFilter, row.ty)) {
@@ -1922,8 +2096,17 @@ function initSearch(rawSearchIndex) {
}
if (parsedQuery.literalSearch) {
- if (searchWord === elem.name) {
- addIntoResults(results_others, fullId, pos, index, 0, path_dist);
+ if (searchWord === elem.normalizedName) {
+ addIntoResults(
+ results_others,
+ fullId,
+ pos,
+ index,
+ 0,
+ path_dist,
+ -1,
+ false
+ );
}
return;
}
@@ -1934,7 +2117,16 @@ function initSearch(rawSearchIndex) {
return;
}
- addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance);
+ addIntoResults(
+ results_others,
+ fullId,
+ pos,
+ index,
+ dist,
+ path_dist,
+ maxEditDistance,
+ false
+ );
}
/**
@@ -1951,25 +2143,80 @@ function initSearch(rawSearchIndex) {
return;
}
- // If the result is too "bad", we return false and it ends this search.
if (!unifyFunctionTypes(
row.type.inputs,
parsedQuery.elems,
row.type.where_clause,
null,
- mgens => {
+ (mgens, matchedPartsParams) => {
return unifyFunctionTypes(
row.type.output,
parsedQuery.returned,
row.type.where_clause,
- mgens
+ mgens,
+ (completeMgens, matchedPartsReturn) => {
+ writeNamesToTypeParameters(completeMgens, row.type, parsedQuery);
+ clearHighlighted(row.type);
+ for (const part of matchedPartsParams) {
+ part.highlighted = true;
+ }
+ for (const part of matchedPartsReturn) {
+ part.highlighted = true;
+ }
+ return true;
+ }
);
}
)) {
return;
}
- addIntoResults(results, row.id, pos, 0, 0, 0, Number.MAX_VALUE);
+ addIntoResults(results, row.id, pos, 0, 0, 0, Number.MAX_VALUE, true);
+ }
+
+ function writeNamesToTypeParameters(mgens, rowType, parsedQuery) {
+ const queryTypeToName = new Map();
+ function getQueryTypeFromList(list) {
+ if (!list) {
+ return;
+ }
+ for (const queryElem of list) {
+ if (queryElem.generics && queryElem.generics.length > 0) {
+ getQueryTypeFromList(queryElem.generics);
+ }
+ if (!queryTypeToName.has(queryElem.id)) {
+ queryTypeToName.set(queryElem.id, queryElem.name);
+ }
+ }
+ }
+ getQueryTypeFromList(parsedQuery.elems);
+ getQueryTypeFromList(parsedQuery.returned);
+ function assignFunctionNameFromList(list) {
+ if (!list) {
+ return;
+ }
+ for (const fnType of list) {
+ if (fnType.id < 0) {
+ assignFunctionNameFromList(rowType.where_clause[(-fnType.id) - 1]);
+ if (mgens.has(fnType.id)) {
+ const queryId = mgens.get(fnType.id);
+ if (queryTypeToName.has(queryId) && queryId < 0) {
+ const queryName = queryTypeToName.get(queryId);
+ fnType.name = queryName;
+ } else {
+ fnType.name = null;
+ }
+ } else {
+ fnType.name = null;
+ }
+ }
+ if (fnType.generics && fnType.generics.length > 0) {
+ assignFunctionNameFromList(fnType.generics);
+ }
+ }
+ }
+ assignFunctionNameFromList(rowType.inputs);
+ assignFunctionNameFromList(rowType.output);
}
function innerRunQuery() {
@@ -1977,10 +2224,10 @@ function initSearch(rawSearchIndex) {
let queryLen = 0;
for (const elem of parsedQuery.elems) {
- queryLen += elem.name.length;
+ queryLen += elem.normalizedName.length;
}
for (const elem of parsedQuery.returned) {
- queryLen += elem.name.length;
+ queryLen += elem.normalizedName.length;
}
const maxEditDistance = Math.floor(queryLen / 3);
@@ -2027,21 +2274,21 @@ function initSearch(rawSearchIndex) {
if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
&& elem.generics.length === 0)
|| elem.typeFilter === TY_GENERIC) {
- if (genericSymbols.has(elem.name)) {
- elem.id = genericSymbols.get(elem.name);
+ if (genericSymbols.has(elem.normalizedName)) {
+ elem.id = genericSymbols.get(elem.normalizedName);
} else {
elem.id = -(genericSymbols.size + 1);
- genericSymbols.set(elem.name, elem.id);
+ genericSymbols.set(elem.normalizedName, elem.id);
}
- if (elem.typeFilter === -1 && elem.name.length >= 3) {
+ if (elem.typeFilter === -1 && elem.normalizedName.length >= 3) {
// Silly heuristic to catch if the user probably meant
// to not write a generic parameter. We don't use it,
// just bring it up.
- const maxPartDistance = Math.floor(elem.name.length / 3);
+ const maxPartDistance = Math.floor(elem.normalizedName.length / 3);
let matchDist = maxPartDistance + 1;
let matchName = "";
for (const name of typeNameIdMap.keys()) {
- const dist = editDistance(name, elem.name, maxPartDistance);
+ const dist = editDistance(name, elem.normalizedName, maxPartDistance);
if (dist <= matchDist && dist <= maxPartDistance) {
if (dist === matchDist && matchName > name) {
continue;
@@ -2051,7 +2298,7 @@ function initSearch(rawSearchIndex) {
}
}
if (matchName !== "") {
- parsedQuery.proposeCorrectionFrom = elem.name;
+ parsedQuery.proposeCorrectionFrom = elem.normalizedName;
parsedQuery.proposeCorrectionTo = matchName;
}
}
@@ -2061,7 +2308,7 @@ function initSearch(rawSearchIndex) {
// Rust does not have HKT
parsedQuery.error = [
"Generic type parameter ",
- elem.name,
+ elem.normalizedName,
" does not accept generic parameters",
];
}
@@ -2098,10 +2345,22 @@ function initSearch(rawSearchIndex) {
elem = parsedQuery.returned[0];
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
- in_returned = row.type && unifyFunctionTypes(
+ if (!row.type) {
+ continue;
+ }
+ in_returned = unifyFunctionTypes(
row.type.output,
parsedQuery.returned,
- row.type.where_clause
+ row.type.where_clause,
+ null,
+ (mgens, parts) => {
+ writeNamesToTypeParameters(mgens, row.type, parsedQuery);
+ clearHighlighted(row.type);
+ for (const part of parts) {
+ part.highlighted = true;
+ }
+ return true;
+ }
);
if (in_returned) {
addIntoResults(
@@ -2110,7 +2369,9 @@ function initSearch(rawSearchIndex) {
i,
-1,
0,
- Number.MAX_VALUE
+ Number.MAX_VALUE,
+ undefined,
+ true
);
}
}
@@ -2126,10 +2387,11 @@ function initSearch(rawSearchIndex) {
innerRunQuery();
}
+ const isOthersTypeSearch = parsedQuery.foundElems > 1 || parsedQuery.returned.length > 0;
const ret = createQueryResults(
sortResults(results_in_args, true, currentCrate),
sortResults(results_returned, true, currentCrate),
- sortResults(results_others, false, currentCrate),
+ sortResults(results_others, isOthersTypeSearch, currentCrate),
parsedQuery);
handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
if (parsedQuery.error !== null && ret.others.length !== 0) {
@@ -2264,11 +2526,12 @@ function initSearch(rawSearchIndex) {
/**
* Render a set of search results for a single tab.
- * @param {Array>} array - The search results for this tab
+ * @param {Array>} array - The search results for this tab
* @param {ParsedQuery} query
* @param {boolean} display - True if this is the active tab
+ * @param {boolean} isType - True shows the function signature
*/
- function addTab(array, query, display) {
+ function addTab(array, query, display, isType) {
let extraClass = "";
if (display === true) {
extraClass = " active";
@@ -2313,6 +2576,20 @@ ${item.displayPath}${name}\
const description = document.createElement("div");
description.className = "desc";
+ if (isType) {
+ const displayTypeSignature = document.createElement("div");
+ item.displayTypeSignature.forEach((value, index) => {
+ if (index % 2 !== 0) {
+ const highlight = document.createElement("strong");
+ highlight.appendChild(document.createTextNode(value));
+ displayTypeSignature.appendChild(highlight);
+ } else {
+ displayTypeSignature.appendChild(document.createTextNode(value));
+ }
+ });
+ displayTypeSignature.className = "type-signature";
+ description.appendChild(displayTypeSignature);
+ }
description.insertAdjacentHTML("beforeend", item.desc);
link.appendChild(description);
@@ -2392,9 +2669,24 @@ ${item.displayPath}${name}\
currentResults = results.query.userQuery;
- const ret_others = addTab(results.others, results.query, true);
- const ret_in_args = addTab(results.in_args, results.query, false);
- const ret_returned = addTab(results.returned, results.query, false);
+ const ret_others = addTab(
+ results.others,
+ results.query,
+ true,
+ results.query.foundElems > 1 || results.query.returned.length > 0
+ );
+ const ret_in_args = addTab(
+ results.in_args,
+ results.query,
+ false,
+ true
+ );
+ const ret_returned = addTab(
+ results.returned,
+ results.query,
+ false,
+ true
+ );
// Navigate to the relevant tab if the current tab is empty, like in case users search
// for "-> String". If they had selected another tab previously, they have to click on
@@ -2426,6 +2718,9 @@ ${item.displayPath}${name}\
let output = `Results${crates}
`;
if (results.query.error !== null) {
const error = results.query.error;
+ if (!error.forEach) {
+ throw error;
+ }
error.forEach((value, index) => {
value = value.split("<").join("<").split(">").join(">");
if (index % 2 !== 0) {
@@ -2569,19 +2864,22 @@ ${item.displayPath}${name}\
*
* @param {null|Array} types
* @param {Array<{name: string, ty: number}>} lowercasePaths
+ * @param {Array<{name: string, ty: number}>} paths
*
* @return {Array}
*/
- function buildItemSearchTypeAll(types, lowercasePaths) {
- return types.map(type => buildItemSearchType(type, lowercasePaths));
+ function buildItemSearchTypeAll(types, lowercasePaths, paths) {
+ return types.map(type => buildItemSearchType(type, lowercasePaths, paths));
}
/**
* Converts a single type.
*
* @param {RawFunctionType} type
+ * @param {Array<{name: string, ty: number}>} lowercasePaths
+ * @param {Array<{name: string, ty: number}>} paths
*/
- function buildItemSearchType(type, lowercasePaths) {
+ function buildItemSearchType(type, lowercasePaths, paths) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
let pathIndex, generics;
@@ -2592,7 +2890,8 @@ ${item.displayPath}${name}\
pathIndex = type[PATH_INDEX_DATA];
generics = buildItemSearchTypeAll(
type[GENERICS_DATA],
- lowercasePaths
+ lowercasePaths,
+ paths
);
}
if (pathIndex < 0) {
@@ -2600,6 +2899,7 @@ ${item.displayPath}${name}\
// the actual names of generic parameters aren't stored, since they aren't API
return {
id: pathIndex,
+ name: null,
ty: TY_GENERIC,
path: null,
generics,
@@ -2609,6 +2909,7 @@ ${item.displayPath}${name}\
// `0` is used as a sentinel because it's fewer bytes than `null`
return {
id: null,
+ name: null,
ty: null,
path: null,
generics,
@@ -2617,6 +2918,7 @@ ${item.displayPath}${name}\
const item = lowercasePaths[pathIndex - 1];
return {
id: buildTypeMapIndex(item.name),
+ name: paths[pathIndex - 1].name,
ty: item.ty,
path: item.path,
generics,
@@ -2635,11 +2937,11 @@ ${item.displayPath}${name}\
*
* @param {RawFunctionSearchType} functionSearchType
* @param {Array<{name: string, ty: number}>} lowercasePaths
- * @param {Map}
+ * @param {Array<{name: string, ty: number}>} paths
*
* @return {null|FunctionSearchType}
*/
- function buildFunctionSearchType(functionSearchType, lowercasePaths) {
+ function buildFunctionSearchType(functionSearchType, lowercasePaths, paths) {
const INPUTS_DATA = 0;
const OUTPUT_DATA = 1;
// `0` is used as a sentinel because it's fewer bytes than `null`
@@ -2648,20 +2950,26 @@ ${item.displayPath}${name}\
}
let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") {
- inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
+ inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths, paths)];
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
- lowercasePaths
+ lowercasePaths,
+ paths
);
}
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
- output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
+ output = [buildItemSearchType(
+ functionSearchType[OUTPUT_DATA],
+ lowercasePaths,
+ paths
+ )];
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
- lowercasePaths
+ lowercasePaths,
+ paths
);
}
} else {
@@ -2671,8 +2979,8 @@ ${item.displayPath}${name}\
const l = functionSearchType.length;
for (let i = 2; i < l; ++i) {
where_clause.push(typeof functionSearchType[i] === "number"
- ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
- : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+ ? [buildItemSearchType(functionSearchType[i], lowercasePaths, paths)]
+ : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths, paths));
}
return {
inputs, output, where_clause,
@@ -2697,6 +3005,8 @@ ${item.displayPath}${name}\
typeNameIdOfArray = buildTypeMapIndex("array");
typeNameIdOfSlice = buildTypeMapIndex("slice");
typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]");
+ typeNameIdOfNever = buildTypeMapIndex("never");
+ typeNameIdOfTuple = buildTypeMapIndex("tuple");
for (const crate in rawSearchIndex) {
if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
@@ -2856,7 +3166,8 @@ ${item.displayPath}${name}\
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType(
itemFunctionSearchTypes[i],
- lowercasePaths
+ lowercasePaths,
+ paths
),
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index c7e6dd3615e94..9d979067d6a63 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -373,8 +373,35 @@ function loadSearchJS(doc_folder, resource_suffix) {
return {
doSearch: function(queryStr, filterCrate, currentCrate) {
- return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
+ const results = searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
filterCrate, currentCrate);
+ for (const key in results) {
+ if (results[key]) {
+ for (const resultKey in results[key]) {
+ if (!Object.prototype.hasOwnProperty.call(results[key], resultKey)) {
+ continue;
+ }
+ const entry = results[key][resultKey];
+ if (!entry) {
+ continue;
+ }
+ if (Object.prototype.hasOwnProperty.call(entry, "displayTypeSignature") &&
+ entry.displayTypeSignature !== null &&
+ entry.displayTypeSignature instanceof Array
+ ) {
+ entry.displayTypeSignature.forEach((value, index) => {
+ if (index % 2 === 1) {
+ entry.displayTypeSignature[index] = "*" + value + "*";
+ } else {
+ entry.displayTypeSignature[index] = value;
+ }
+ });
+ entry.displayTypeSignature = entry.displayTypeSignature.join("");
+ }
+ }
+ }
+ }
+ return results;
},
getCorrections: function(queryStr, filterCrate, currentCrate) {
const parsedQuery = searchModule.parseQuery(queryStr);
diff --git a/tests/rustdoc-js/generics-impl.js b/tests/rustdoc-js/generics-impl.js
index 5e33e224876fe..1e9d57dcf6e85 100644
--- a/tests/rustdoc-js/generics-impl.js
+++ b/tests/rustdoc-js/generics-impl.js
@@ -4,33 +4,61 @@ const EXPECTED = [
{
'query': 'Aaaaaaa -> u32',
'others': [
- { 'path': 'generics_impl::Aaaaaaa', 'name': 'bbbbbbb' },
+ {
+ 'path': 'generics_impl::Aaaaaaa',
+ 'name': 'bbbbbbb',
+ 'displayTypeSignature': '*Aaaaaaa* -> *u32*'
+ },
],
},
{
'query': 'Aaaaaaa -> bool',
'others': [
- { 'path': 'generics_impl::Aaaaaaa', 'name': 'ccccccc' },
+ {
+ 'path': 'generics_impl::Aaaaaaa',
+ 'name': 'ccccccc',
+ 'displayTypeSignature': '*Aaaaaaa* -> *bool*'
+ },
],
},
{
'query': 'Aaaaaaa -> usize',
'others': [
- { 'path': 'generics_impl::Aaaaaaa', 'name': 'read' },
+ {
+ 'path': 'generics_impl::Aaaaaaa',
+ 'name': 'read',
+ 'displayTypeSignature': '*Aaaaaaa*, [] -> Result<*usize*>'
+ },
],
},
{
'query': 'Read -> u64',
'others': [
- { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
- { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'eeeeeee',
+ 'displayTypeSignature': 'impl *Read* -> *u64*'
+ },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'ggggggg',
+ 'displayTypeSignature': 'Ddddddd -> *u64*'
+ },
],
},
{
'query': 'trait:Read -> u64',
'others': [
- { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
- { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'eeeeeee',
+ 'displayTypeSignature': 'impl *Read* -> *u64*'
+ },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'ggggggg',
+ 'displayTypeSignature': 'Ddddddd -> *u64*'
+ },
],
},
{
@@ -40,19 +68,31 @@ const EXPECTED = [
{
'query': 'bool -> u64',
'others': [
- { 'path': 'generics_impl::Ddddddd', 'name': 'fffffff' },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'fffffff',
+ 'displayTypeSignature': '*bool* -> *u64*'
+ },
],
},
{
'query': 'Ddddddd -> u64',
'others': [
- { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'ggggggg',
+ 'displayTypeSignature': '*Ddddddd* -> *u64*'
+ },
],
},
{
'query': '-> Ddddddd',
'others': [
- { 'path': 'generics_impl::Ddddddd', 'name': 'hhhhhhh' },
+ {
+ 'path': 'generics_impl::Ddddddd',
+ 'name': 'hhhhhhh',
+ 'displayTypeSignature': '-> *Ddddddd*'
+ },
],
},
];
diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js
index edce4268c5ac7..247e66697d5d9 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.js
+++ b/tests/rustdoc-js/generics-match-ambiguity.js
@@ -8,15 +8,31 @@ const EXPECTED = [
{
'query': 'Wrap',
'in_args': [
- { 'path': 'generics_match_ambiguity', 'name': 'bar' },
- { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+ {
+ 'path': 'generics_match_ambiguity',
+ 'name': 'bar',
+ 'displayTypeSignature': '*Wrap*, Wrap'
+ },
+ {
+ 'path': 'generics_match_ambiguity',
+ 'name': 'foo',
+ 'displayTypeSignature': '*Wrap*, Wrap'
+ },
],
},
{
'query': 'Wrap',
'in_args': [
- { 'path': 'generics_match_ambiguity', 'name': 'bar' },
- { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+ {
+ 'path': 'generics_match_ambiguity',
+ 'name': 'bar',
+ 'displayTypeSignature': '*Wrap*<*i32*, u32>, Wrap'
+ },
+ {
+ 'path': 'generics_match_ambiguity',
+ 'name': 'foo',
+ 'displayTypeSignature': '*Wrap*<*i32*>, Wrap'
+ },
],
},
{
diff --git a/tests/rustdoc-js/type-parameters.js b/tests/rustdoc-js/type-parameters.js
index e695f189bb672..e637cb4fb254e 100644
--- a/tests/rustdoc-js/type-parameters.js
+++ b/tests/rustdoc-js/type-parameters.js
@@ -5,79 +5,79 @@ const EXPECTED = [
{
query: '-> trait:Some',
others: [
- { path: 'foo', name: 'alef' },
- { path: 'foo', name: 'alpha' },
+ { path: 'foo', name: 'alef', displayTypeSignature: '-> impl *Some*' },
+ { path: 'foo', name: 'alpha', displayTypeSignature: '-> impl *Some*' },
],
},
{
query: '-> generic:T',
others: [
- { path: 'foo', name: 'bet' },
- { path: 'foo', name: 'alef' },
- { path: 'foo', name: 'beta' },
+ { path: 'foo', name: 'bet', displayTypeSignature: '_ -> *T*' },
+ { path: 'foo', name: 'alef', displayTypeSignature: '-> *T*' },
+ { path: 'foo', name: 'beta', displayTypeSignature: 'T -> *T*' },
],
},
{
query: 'A -> B',
others: [
- { path: 'foo', name: 'bet' },
+ { path: 'foo', name: 'bet', displayTypeSignature: '*A* -> *B*' },
],
},
{
query: 'A -> A',
others: [
- { path: 'foo', name: 'beta' },
+ { path: 'foo', name: 'beta', displayTypeSignature: '*A* -> *A*' },
],
},
{
query: 'A, A',
others: [
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: '*A*, *A*' },
],
},
{
query: 'A, B',
others: [
- { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'other', displayTypeSignature: '*A*, *B*' },
],
},
{
query: 'Other, Other',
others: [
- { path: 'foo', name: 'other' },
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'other', displayTypeSignature: 'impl *Other*, impl *Other*' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: 'impl *Other*, impl *Other*' },
],
},
{
query: 'generic:T',
in_args: [
- { path: 'foo', name: 'bet' },
- { path: 'foo', name: 'beta' },
- { path: 'foo', name: 'other' },
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'bet', displayTypeSignature: '*T* -> _' },
+ { path: 'foo', name: 'beta', displayTypeSignature: '*T* -> T' },
+ { path: 'foo', name: 'other', displayTypeSignature: '*T*, _' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: '*T*, T' },
],
},
{
query: 'generic:Other',
in_args: [
- { path: 'foo', name: 'bet' },
- { path: 'foo', name: 'beta' },
- { path: 'foo', name: 'other' },
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'bet', displayTypeSignature: '*Other* -> _' },
+ { path: 'foo', name: 'beta', displayTypeSignature: '*Other* -> Other' },
+ { path: 'foo', name: 'other', displayTypeSignature: '*Other*, _' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: '*Other*, Other' },
],
},
{
query: 'trait:Other',
in_args: [
- { path: 'foo', name: 'other' },
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'other', displayTypeSignature: '_, impl *Other*' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: 'impl *Other*, impl *Other*' },
],
},
{
query: 'Other',
in_args: [
- { path: 'foo', name: 'other' },
- { path: 'foo', name: 'alternate' },
+ { path: 'foo', name: 'other', displayTypeSignature: '_, impl *Other*' },
+ { path: 'foo', name: 'alternate', displayTypeSignature: 'impl *Other*, impl *Other*' },
],
},
{