diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustdoc/html/static/js/search.js | 164 | ||||
| -rw-r--r-- | src/tools/rustdoc-js/tester.js | 11 |
2 files changed, 120 insertions, 55 deletions
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 36ff20e299e..840ed8e1080 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -354,12 +354,15 @@ function initSearch(rawSearchIndex) { if (isInGenerics) { parserState.genericsElems += 1; } + const typeFilter = parserState.typeFilter; + parserState.typeFilter = null; return { name: name, fullPath: pathSegments, pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), pathLast: pathSegments[pathSegments.length - 1], generics: generics, + typeFilter, }; } @@ -495,6 +498,11 @@ function initSearch(rawSearchIndex) { */ function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; + let start = parserState.pos; + + // If this is a generic, keep the outer item's type filter around. + const oldTypeFilter = parserState.typeFilter; + parserState.typeFilter = null; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; @@ -506,7 +514,25 @@ function initSearch(rawSearchIndex) { continue; } else if (c === ":" && isPathStart(parserState)) { throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; - } else if (c === ":" || isEndCharacter(c)) { + } else if (c === ":") { + if (parserState.typeFilter !== null) { + throw ["Unexpected ", ":"]; + } + if (elems.length === 0) { + throw ["Expected type filter before ", ":"]; + } else if (query.literalSearch) { + throw ["You cannot use quotes on type filter"]; + } + // 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.pos += 1; + parserState.totalElems -= 1; + query.literalSearch = false; + foundStopChar = true; + continue; + } else if (isEndCharacter(c)) { let extra = ""; if (endChar === ">") { extra = "<"; @@ -540,15 +566,10 @@ function initSearch(rawSearchIndex) { ]; } const posBefore = parserState.pos; + start = parserState.pos; getNextElem(query, parserState, elems, endChar === ">"); - if (endChar !== "") { - if (parserState.pos >= parserState.length) { - throw ["Unclosed ", "<"]; - } - const c2 = parserState.userQuery[parserState.pos]; - if (!isSeparatorCharacter(c2) && c2 !== endChar) { - throw ["Expected ", endChar, ", found ", c2]; - } + if (endChar !== "" && parserState.pos >= parserState.length) { + throw ["Unclosed ", "<"]; } // This case can be encountered if `getNextElem` encountered a "stop character" right // from the start. For example if you have `,,` or `<>`. In this case, we simply move up @@ -564,6 +585,8 @@ function initSearch(rawSearchIndex) { // We are either at the end of the string or on the `endChar` character, let's move forward // in any case. parserState.pos += 1; + + parserState.typeFilter = oldTypeFilter; } /** @@ -572,10 +595,10 @@ function initSearch(rawSearchIndex) { * * @param {ParserState} parserState */ - function checkExtraTypeFilterCharacters(parserState) { + function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery; - for (let pos = 0; pos < parserState.pos; ++pos) { + for (let pos = start; pos < parserState.pos; ++pos) { if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) { throw ["Unexpected ", query[pos], " in type filter"]; } @@ -591,6 +614,7 @@ function initSearch(rawSearchIndex) { */ function parseInput(query, parserState) { let foundStopChar = true; + let start = parserState.pos; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; @@ -612,16 +636,15 @@ function initSearch(rawSearchIndex) { } if (query.elems.length === 0) { throw ["Expected type filter before ", ":"]; - } else if (query.elems.length !== 1 || parserState.totalElems !== 1) { - throw ["Unexpected ", ":"]; } else if (query.literalSearch) { throw ["You cannot use quotes on type filter"]; } - checkExtraTypeFilterCharacters(parserState); // The type filter doesn't count as an element since it's a modifier. - parserState.typeFilter = query.elems.pop().name; + const typeFilterElem = query.elems.pop(); + checkExtraTypeFilterCharacters(start, parserState); + parserState.typeFilter = typeFilterElem.name; parserState.pos += 1; - parserState.totalElems = 0; + parserState.totalElems -= 1; query.literalSearch = false; foundStopChar = true; continue; @@ -653,6 +676,7 @@ function initSearch(rawSearchIndex) { ]; } const before = query.elems.length; + start = parserState.pos; getNextElem(query, parserState, query.elems, false); if (query.elems.length === before) { // Nothing was added, weird... Let's increase the position to not remain stuck. @@ -660,6 +684,9 @@ function initSearch(rawSearchIndex) { } foundStopChar = false; } + if (parserState.typeFilter !== null) { + throw ["Unexpected ", ":", " (expected path after type filter)"]; + } while (parserState.pos < parserState.length) { if (isReturnArrow(parserState)) { parserState.pos += 2; @@ -687,7 +714,6 @@ function initSearch(rawSearchIndex) { return { original: userQuery, userQuery: userQuery.toLowerCase(), - typeFilter: NO_TYPE_FILTER, elems: [], returned: [], // Total number of "top" elements (does not include generics). @@ -738,8 +764,8 @@ function initSearch(rawSearchIndex) { * * ident = *(ALPHA / DIGIT / "_") * path = ident *(DOUBLE-COLON ident) [!] - * arg = path [generics] - * arg-without-generic = path + * arg = [type-filter *WS COLON *WS] path [generics] + * arg-without-generic = [type-filter *WS COLON *WS] path * type-sep = COMMA/WS *(COMMA/WS) * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) * nonempty-arg-list-without-generics = *(type-sep) arg-without-generic @@ -749,7 +775,7 @@ function initSearch(rawSearchIndex) { * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list * * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] - * type-search = [type-filter *WS COLON] [ nonempty-arg-list ] [ return-args ] + * type-search = [ nonempty-arg-list ] [ return-args ] * * query = *WS (exact-search / type-search) *WS * @@ -798,6 +824,20 @@ function initSearch(rawSearchIndex) { * @return {ParsedQuery} - The parsed query */ function parseQuery(userQuery) { + function convertTypeFilterOnElem(elem) { + if (elem.typeFilter !== null) { + let typeFilter = elem.typeFilter; + if (typeFilter === "const") { + typeFilter = "constant"; + } + elem.typeFilter = itemTypeFromName(typeFilter); + } else { + elem.typeFilter = NO_TYPE_FILTER; + } + for (const elem2 of elem.generics) { + convertTypeFilterOnElem(elem2); + } + } userQuery = userQuery.trim(); const parserState = { length: userQuery.length, @@ -812,17 +852,15 @@ function initSearch(rawSearchIndex) { try { parseInput(query, parserState); - if (parserState.typeFilter !== null) { - let typeFilter = parserState.typeFilter; - if (typeFilter === "const") { - typeFilter = "constant"; - } - query.typeFilter = itemTypeFromName(typeFilter); + for (const elem of query.elems) { + convertTypeFilterOnElem(elem); + } + for (const elem of query.returned) { + convertTypeFilterOnElem(elem); } } catch (err) { query = newParsedQuery(userQuery); query.error = err; - query.typeFilter = -1; return query; } @@ -1057,12 +1095,10 @@ function initSearch(rawSearchIndex) { } // The names match, but we need to be sure that all generics kinda // match as well. - let elem_name; if (elem.generics.length > 0 && row.generics.length >= elem.generics.length) { const elems = Object.create(null); for (const entry of row.generics) { - elem_name = entry.name; - if (elem_name === "") { + if (entry.name === "") { // Pure generic, needs to check into it. if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance) !== 0) { @@ -1070,19 +1106,19 @@ function initSearch(rawSearchIndex) { } continue; } - if (elems[elem_name] === undefined) { - elems[elem_name] = 0; + if (elems[entry.name] === undefined) { + elems[entry.name] = []; } - elems[elem_name] += 1; + elems[entry.name].push(entry.ty); } // We need to find the type that matches the most to remove it in order // to move forward. - for (const generic of elem.generics) { + const handleGeneric = generic => { let match = null; if (elems[generic.name]) { match = generic.name; } else { - for (elem_name in elems) { + for (const elem_name in elems) { if (!hasOwnPropertyRustdoc(elems, elem_name)) { continue; } @@ -1093,12 +1129,32 @@ function initSearch(rawSearchIndex) { } } if (match === null) { - return maxEditDistance + 1; + return false; } - elems[match] -= 1; - if (elems[match] === 0) { + const matchIdx = elems[match].findIndex(tmp_elem => + typePassesFilter(generic.typeFilter, tmp_elem)); + if (matchIdx === -1) { + return false; + } + elems[match].splice(matchIdx, 1); + if (elems[match].length === 0) { delete elems[match]; } + return true; + }; + // To do the right thing with type filters, we first process generics + // that have them, removing matching ones from the "bag," then do the + // ones with no type filter, which can match any entry regardless of its + // own type. + for (const generic of elem.generics) { + if (generic.typeFilter !== -1 && !handleGeneric(generic)) { + return maxEditDistance + 1; + } + } + for (const generic of elem.generics) { + if (generic.typeFilter === -1 && !handleGeneric(generic)) { + return maxEditDistance + 1; + } } return 0; } @@ -1145,14 +1201,20 @@ function initSearch(rawSearchIndex) { return maxEditDistance + 1; } - let dist = editDistance(row.name, elem.name, maxEditDistance); + let dist; + if (typePassesFilter(elem.typeFilter, row.ty)) { + dist = editDistance(row.name, elem.name, maxEditDistance); + } else { + dist = maxEditDistance + 1; + } if (literalSearch) { if (dist !== 0) { // The name didn't match, let's try to check if the generics do. if (elem.generics.length === 0) { const checkGeneric = row.generics.length > 0; if (checkGeneric && row.generics - .findIndex(tmp_elem => tmp_elem.name === elem.name) !== -1) { + .findIndex(tmp_elem => tmp_elem.name === elem.name && + typePassesFilter(elem.typeFilter, tmp_elem.ty)) !== -1) { return 0; } } @@ -1201,22 +1263,21 @@ function initSearch(rawSearchIndex) { * * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. - * @param {integer} typeFilter + * @param {integer} maxEditDistance * @param {Array<integer>} skipPositions - Do not return one of these positions. * * @return {dist: integer, position: integer} - Returns an edit distance to the best match. * If there is no match, returns * `maxEditDistance + 1` and position: -1. */ - function findArg(row, elem, typeFilter, maxEditDistance, skipPositions) { + function findArg(row, elem, maxEditDistance, skipPositions) { let dist = maxEditDistance + 1; let position = -1; if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { let i = 0; for (const input of row.type.inputs) { - if (!typePassesFilter(typeFilter, input.ty) || - skipPositions.indexOf(i) !== -1) { + if (skipPositions.indexOf(i) !== -1) { i += 1; continue; } @@ -1245,14 +1306,14 @@ function initSearch(rawSearchIndex) { * * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. - * @param {integer} typeFilter + * @param {integer} maxEditDistance * @param {Array<integer>} skipPositions - Do not return one of these positions. * * @return {dist: integer, position: integer} - Returns an edit distance to the best match. * If there is no match, returns * `maxEditDistance + 1` and position: -1. */ - function checkReturned(row, elem, typeFilter, maxEditDistance, skipPositions) { + function checkReturned(row, elem, maxEditDistance, skipPositions) { let dist = maxEditDistance + 1; let position = -1; @@ -1260,8 +1321,7 @@ function initSearch(rawSearchIndex) { const ret = row.type.output; let i = 0; for (const ret_ty of ret) { - if (!typePassesFilter(typeFilter, ret_ty.ty) || - skipPositions.indexOf(i) !== -1) { + if (skipPositions.indexOf(i) !== -1) { i += 1; continue; } @@ -1483,15 +1543,15 @@ function initSearch(rawSearchIndex) { const fullId = row.id; const searchWord = searchWords[pos]; - const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance, []); - const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance, []); + const in_args = findArg(row, elem, maxEditDistance, []); + const returned = checkReturned(row, elem, maxEditDistance, []); // path_dist is 0 because no parent path information is currently stored // in the search index addIntoResults(results_in_args, fullId, pos, -1, in_args.dist, 0, maxEditDistance); addIntoResults(results_returned, fullId, pos, -1, returned.dist, 0, maxEditDistance); - if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { + if (!typePassesFilter(elem.typeFilter, row.ty)) { return; } @@ -1568,7 +1628,6 @@ function initSearch(rawSearchIndex) { const { dist, position } = callback( row, elem, - NO_TYPE_FILTER, maxEditDistance, skipPositions ); @@ -1632,7 +1691,6 @@ function initSearch(rawSearchIndex) { in_returned = checkReturned( row, elem, - parsedQuery.typeFilter, maxEditDistance, [] ); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 8d46a8ce7f1..6b9a9b66a7d 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -79,11 +79,18 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position) "foundElems", "original", "returned", - "typeFilter", "userQuery", "error", ]; - } else if (fullPath.endsWith("elems") || fullPath.endsWith("generics")) { + } else if (fullPath.endsWith("elems") || fullPath.endsWith("returned")) { + fieldsToCheck = [ + "name", + "fullPath", + "pathWithoutLast", + "pathLast", + "generics", + ]; + } else if (fullPath.endsWith("generics")) { fieldsToCheck = [ "name", "fullPath", |
