diff options
| author | Guillaume Gomez <guillaume.gomez@huawei.com> | 2023-06-12 13:23:38 +0200 |
|---|---|---|
| committer | Guillaume Gomez <guillaume.gomez@huawei.com> | 2023-06-14 14:22:17 +0200 |
| commit | b99bf4677bb157ad46758099189389ac8cc60779 (patch) | |
| tree | e1c76f0f87304695c3e428470797b999d1357716 | |
| parent | 57c215b08e18054c64428e00a291b45dc690d9de (diff) | |
| download | rust-b99bf4677bb157ad46758099189389ac8cc60779.tar.gz rust-b99bf4677bb157ad46758099189389ac8cc60779.zip | |
Use whitespace as path separator like double colon
| -rw-r--r-- | src/librustdoc/html/static/js/search.js | 234 |
1 files changed, 163 insertions, 71 deletions
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1ccfca8d0d5..d421adb13cb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -261,7 +261,7 @@ function initSearch(rawSearchIndex) { } function isStopCharacter(c) { - return isWhitespace(c) || isEndCharacter(c); + return isEndCharacter(c); } function isErrorCharacter(c) { @@ -357,18 +357,69 @@ function initSearch(rawSearchIndex) { * @return {boolean} */ function isSeparatorCharacter(c) { - return c === "," || isWhitespaceCharacter(c); + return c === ","; } - /** - * Returns `true` if the given `c` character is a whitespace. +/** + * Returns `true` if the given `c` character is a path separator. For example + * `:` in `a::b` or a whitespace in `a b`. * * @param {string} c * * @return {boolean} */ - function isWhitespaceCharacter(c) { - return c === " " || c === "\t"; + function isPathSeparator(c) { + return c === ":" || isWhitespace(c); + } + + /** + * Returns `true` if the previous character is `lookingFor`. + * + * @param {ParserState} parserState + * @param {String} lookingFor + * + * @return {boolean} + */ + function prevIs(parserState, lookingFor) { + let pos = parserState.pos; + while (pos > 0) { + const c = parserState.userQuery[pos - 1]; + if (c === lookingFor) { + return true; + } else if (!isWhitespace(c)) { + break; + } + pos -= 1; + } + return false; + } + + /** + * Returns `true` if the last element in the `elems` argument has generics. + * + * @param {Array<QueryElement>} elems + * @param {ParserState} parserState + * + * @return {boolean} + */ + function isLastElemGeneric(elems, parserState) { + return (elems.length > 0 && elems[elems.length - 1].generics.length > 0) || + prevIs(parserState, ">"); + } + + /** + * Increase current parser position until it doesn't find a whitespace anymore. + * + * @param {ParserState} parserState + */ + function skipWhitespace(parserState) { + while (parserState.pos < parserState.userQuery.length) { + const c = parserState.userQuery[parserState.pos]; + if (!isWhitespace(c)) { + break; + } + parserState.pos += 1; + } } /** @@ -380,11 +431,14 @@ function initSearch(rawSearchIndex) { * @return {QueryElement} - The newly created `QueryElement`. */ function createQueryElement(query, parserState, name, generics, isInGenerics) { - if (name === "*" || (name.length === 0 && generics.length === 0)) { - return; + const path = name.trim(); + if (path.length === 0 && generics.length === 0) { + throw ["Unexpected ", parserState.userQuery[parserState.pos]]; + } else if (path === "*") { + throw ["Unexpected ", "*"]; } if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { - throw ["You cannot have more than one element if you use quotes"]; + throw ["Cannot have more than one element if you use quotes"]; } const typeFilter = parserState.typeFilter; parserState.typeFilter = null; @@ -415,38 +469,40 @@ function initSearch(rawSearchIndex) { typeFilter: "primitive", }; } - const pathSegments = name.split("::"); - if (pathSegments.length > 1) { - for (let i = 0, len = pathSegments.length; i < len; ++i) { - const pathSegment = pathSegments[i]; - - if (pathSegment.length === 0) { - if (i === 0) { - throw ["Paths cannot start with ", "::"]; - } else if (i + 1 === len) { - throw ["Paths cannot end with ", "::"]; - } - throw ["Unexpected ", "::::"]; - } - - if (pathSegment === "!") { - pathSegments[i] = "never"; - if (i !== 0) { - throw ["Never type ", "!", " is not associated item"]; - } - } - } - } + if (path.startsWith("::")) { + throw ["Paths cannot start with ", "::"]; + } else if (path.endsWith("::")) { + throw ["Paths cannot end with ", "::"]; + } else if (path.includes("::::")) { + throw ["Unexpected ", "::::"]; + } else if (path.includes(" ::")) { + throw ["Unexpected ", " ::"]; + } else if (path.includes(":: ")) { + throw ["Unexpected ", ":: "]; + } + const pathSegments = path.split(/::|\s+/); // In case we only have something like `<p>`, there is no name. if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { - throw ["Found generics without a path"]; + if (generics.length > 0 || prevIs(parserState, ">")) { + throw ["Found generics without a path"]; + } else { + throw ["Unexpected ", parserState.userQuery[parserState.pos]]; + } + } + for (const [i, pathSegment] of pathSegments.entries()) { + if (pathSegment === "!") { + if (i !== 0) { + throw ["Never type ", "!", " is not associated item"]; + } + pathSegments[i] = "never"; + } } parserState.totalElems += 1; if (isInGenerics) { parserState.genericsElems += 1; } return { - name: name, + name: name.trim(), id: -1, fullPath: pathSegments, pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), @@ -482,15 +538,21 @@ function initSearch(rawSearchIndex) { foundExclamation = parserState.pos; } else if (isErrorCharacter(c)) { throw ["Unexpected ", c]; - } else if ( - isStopCharacter(c) || - isSpecialStartCharacter(c) || - isSeparatorCharacter(c) - ) { - break; - } else if (c === ":") { // If we allow paths ("str::string" for example). - if (!isPathStart(parserState)) { - break; + } else if (isPathSeparator(c)) { + if (c === ":") { + if (!isPathStart(parserState)) { + break; + } + // Skip current ":". + parserState.pos += 1; + } else { + while (parserState.pos + 1 < parserState.length) { + const next_c = parserState.userQuery[parserState.pos + 1]; + if (!isWhitespace(next_c)) { + break; + } + parserState.pos += 1; + } } if (foundExclamation !== -1) { if (foundExclamation !== start && @@ -503,8 +565,13 @@ function initSearch(rawSearchIndex) { foundExclamation = -1; } } - // Skip current ":". - parserState.pos += 1; + } else if ( + c === "[" || + isStopCharacter(c) || + isSpecialStartCharacter(c) || + isSeparatorCharacter(c) + ) { + break; } else { throw ["Unexpected ", c]; } @@ -542,6 +609,7 @@ function initSearch(rawSearchIndex) { function getNextElem(query, parserState, elems, isInGenerics) { const generics = []; + skipWhitespace(parserState); let start = parserState.pos; let end; if (parserState.userQuery[parserState.pos] === "[") { @@ -572,8 +640,9 @@ function initSearch(rawSearchIndex) { typeFilter: "primitive", }); } else { + const isStringElem = parserState.userQuery[start] === "\""; // We handle the strings on their own mostly to make code easier to follow. - if (parserState.userQuery[parserState.pos] === "\"") { + if (isStringElem) { start += 1; getStringElem(query, parserState, isInGenerics); end = parserState.pos - 1; @@ -589,6 +658,9 @@ function initSearch(rawSearchIndex) { parserState.pos += 1; getItemsBefore(query, parserState, generics, ">"); } + if (isStringElem) { + skipWhitespace(parserState); + } if (start >= end && generics.length === 0) { return; } @@ -653,7 +725,7 @@ function initSearch(rawSearchIndex) { if (elems.length === 0) { throw ["Expected type filter before ", ":"]; } else if (query.literalSearch) { - throw ["You cannot use quotes on type filter"]; + throw ["Cannot use quotes on type filter"]; } // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); @@ -668,23 +740,27 @@ function initSearch(rawSearchIndex) { throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { + let extra = []; + if (isLastElemGeneric(query.elems, parserState)) { + extra = [" after ", ">"]; + } else if (prevIs(parserState, "\"")) { + throw ["Cannot have more than one element if you use quotes"]; + } if (endChar !== "") { throw [ "Expected ", - ",", // comma - ", ", - " ", // whitespace + ",", " or ", endChar, + ...extra, ", found ", c, ]; } throw [ "Expected ", - ",", // comma - " or ", - " ", // whitespace + ",", + ...extra, ", found ", c, ]; @@ -720,11 +796,17 @@ function initSearch(rawSearchIndex) { * @param {ParserState} parserState */ function checkExtraTypeFilterCharacters(start, parserState) { - const query = parserState.userQuery; + const query = parserState.userQuery.slice(start, parserState.pos).trim(); - for (let pos = start; pos < parserState.pos; ++pos) { - if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) { - throw ["Unexpected ", query[pos], " in type filter"]; + for (const c in query) { + if (!isIdentCharacter(query[c])) { + throw [ + "Unexpected ", + query[c], + " in type filter (before ", + ":", + ")", + ]; } } } @@ -757,11 +839,10 @@ function initSearch(rawSearchIndex) { } else if (c === ":" && !isPathStart(parserState)) { if (parserState.typeFilter !== null) { throw ["Unexpected ", ":"]; - } - if (query.elems.length === 0) { + } else if (query.elems.length === 0) { throw ["Expected type filter before ", ":"]; } else if (query.literalSearch) { - throw ["You cannot use quotes on type filter"]; + throw ["Cannot use quotes on type filter"]; } // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = query.elems.pop(); @@ -774,27 +855,31 @@ function initSearch(rawSearchIndex) { continue; } if (!foundStopChar) { + let extra = ""; + if (isLastElemGeneric(query.elems, parserState)) { + extra = [" after ", ">"]; + } else if (prevIs(parserState, "\"")) { + throw ["Cannot have more than one element if you use quotes"]; + } if (parserState.typeFilter !== null) { throw [ "Expected ", - ",", // comma - ", ", - " ", // whitespace + ",", " or ", - "->", // arrow + "->", + ...extra, ", found ", c, ]; } throw [ "Expected ", - ",", // comma - ", ", - " ", // whitespace + ",", ", ", - ":", // colon + ":", " or ", - "->", // arrow + "->", + ...extra, ", found ", c, ]; @@ -809,11 +894,18 @@ function initSearch(rawSearchIndex) { foundStopChar = false; } if (parserState.typeFilter !== null) { - throw ["Unexpected ", ":", " (expected path after type filter)"]; + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; } while (parserState.pos < parserState.length) { if (isReturnArrow(parserState)) { parserState.pos += 2; + skipWhitespace(parserState); // Get returned elements. getItemsBefore(query, parserState, query.returned, ""); // Nothing can come afterward! @@ -888,10 +980,10 @@ function initSearch(rawSearchIndex) { * The supported syntax by this parser is as follow: * * ident = *(ALPHA / DIGIT / "_") - * path = ident *(DOUBLE-COLON ident) [!] + * path = ident *(DOUBLE-COLON/{WS} ident) [!] * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET * arg = [type-filter *WS COLON *WS] (path [generics] / slice) - * type-sep = COMMA/WS *(COMMA/WS) + * type-sep = COMMA *(COMMA) * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep) * CLOSE-ANGLE-BRACKET |
