From b99bf4677bb157ad46758099189389ac8cc60779 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Jun 2023 13:23:38 +0200 Subject: [PATCH 1/7] Use whitespace as path separator like double colon --- src/librustdoc/html/static/js/search.js | 234 +++++++++++++++++------- 1 file changed, 163 insertions(+), 71 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1ccfca8d0d5ff..d421adb13cbf2 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} 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 `

`, 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 From b5f8258497d1a1a4b9d5a006e6880a3159ef6ddb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Jun 2023 16:05:00 +0200 Subject: [PATCH 2/7] Update rustdoc-js-std tests --- tests/rustdoc-js-std/parser-errors.js | 96 +++++++++++++++----- tests/rustdoc-js-std/parser-filter.js | 2 +- tests/rustdoc-js-std/parser-generics.js | 6 +- tests/rustdoc-js-std/parser-quote.js | 2 +- tests/rustdoc-js-std/parser-separators.js | 80 +++++++--------- tests/rustdoc-js-std/parser-slice-array.js | 6 +- tests/rustdoc-js-std/parser-weird-queries.js | 40 ++------ 7 files changed, 127 insertions(+), 105 deletions(-) diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index af7f63f99cbdf..66b0e516b2b17 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -33,15 +33,24 @@ const PARSED = [ original: "\"P\" \"P\"", returned: [], userQuery: "\"p\" \"p\"", + error: "Cannot have more than one element if you use quotes", + }, + { + query: '"P","P"', + elems: [], + foundElems: 0, + original: "\"P\",\"P\"", + returned: [], + userQuery: "\"p\",\"p\"", error: "Cannot have more than one literal search element", }, { - query: 'P "P"', + query: "P,\"P\"", elems: [], foundElems: 0, - original: "P \"P\"", + original: "P,\"P\"", returned: [], - userQuery: "p \"p\"", + userQuery: "p,\"p\"", error: "Cannot use literal search when there is more than one element", }, { @@ -51,7 +60,16 @@ const PARSED = [ original: "\"p\" p", returned: [], userQuery: "\"p\" p", - error: "You cannot have more than one element if you use quotes", + error: "Cannot have more than one element if you use quotes", + }, + { + query: '"p",p', + elems: [], + foundElems: 0, + original: "\"p\",p", + returned: [], + userQuery: "\"p\",p", + error: "Cannot have more than one element if you use quotes", }, { query: '"const": p', @@ -60,7 +78,7 @@ const PARSED = [ original: "\"const\": p", returned: [], userQuery: "\"const\": p", - error: "You cannot use quotes on type filter", + error: "Cannot use quotes on type filter", }, { query: "a<:a>", @@ -107,6 +125,15 @@ const PARSED = [ userQuery: "::a::b", error: "Paths cannot start with `::`", }, + { + query: " ::a::b", + elems: [], + foundElems: 0, + original: "::a::b", + returned: [], + userQuery: "::a::b", + error: "Paths cannot start with `::`", + }, { query: "a::::b", elems: [], @@ -135,13 +162,13 @@ const PARSED = [ error: "Expected type filter before `:`", }, { - query: "a b:", + query: "a,b:", elems: [], foundElems: 0, - original: "a b:", + original: "a,b:", returned: [], - userQuery: "a b:", - error: "Unexpected `:` (expected path after type filter)", + userQuery: "a,b:", + error: "Unexpected `:` (expected path after type filter `b:`)", }, { query: "a (b:", @@ -159,7 +186,7 @@ const PARSED = [ original: "_:", returned: [], userQuery: "_:", - error: "Unexpected `:` (expected path after type filter)", + error: "Unexpected `:` (expected path after type filter `_:`)", }, { query: "_:a", @@ -213,6 +240,15 @@ const PARSED = [ original: '"p" ', returned: [], userQuery: '"p" ', + error: "Cannot have more than one element if you use quotes", + }, + { + query: '"p",', + elems: [], + foundElems: 0, + original: '"p",', + returned: [], + userQuery: '"p",', error: "Found generics without a path", }, { @@ -222,7 +258,16 @@ const PARSED = [ original: '"p" a', returned: [], userQuery: '"p" a', - error: "You cannot have more than one element if you use quotes", + error: "Cannot have more than one element if you use quotes", + }, + { + query: '"p",a', + elems: [], + foundElems: 0, + original: '"p",a', + returned: [], + userQuery: '"p",a', + error: "Cannot have more than one element if you use quotes", }, { query: "a,<", @@ -240,7 +285,7 @@ const PARSED = [ original: 'aaaaa<>b', returned: [], userQuery: 'aaaaa<>b', - error: 'Expected `,`, ` `, `:` or `->`, found `b`', + error: 'Expected `,`, `:` or `->` after `>`, found `b`', }, { query: "fn:aaaaa<>b", @@ -249,7 +294,7 @@ const PARSED = [ original: 'fn:aaaaa<>b', returned: [], userQuery: 'fn:aaaaa<>b', - error: 'Expected `,`, ` `, `:` or `->`, found `b`', + error: 'Expected `,`, `:` or `->` after `>`, found `b`', }, { query: "->a<>b", @@ -258,7 +303,7 @@ const PARSED = [ original: '->a<>b', returned: [], userQuery: '->a<>b', - error: 'Expected `,` or ` `, found `b`', + error: 'Expected `,` after `>`, found `b`', }, { query: "a<->", @@ -276,7 +321,7 @@ const PARSED = [ original: 'a:: a', returned: [], userQuery: 'a:: a', - error: 'Paths cannot end with `::`', + error: 'Unexpected `:: `', }, { query: "a ::a", @@ -285,7 +330,7 @@ const PARSED = [ original: 'a ::a', returned: [], userQuery: 'a ::a', - error: 'Paths cannot start with `::`', + error: 'Unexpected ` ::`', }, { query: "a:", @@ -294,7 +339,7 @@ const PARSED = [ original: "a:", returned: [], userQuery: "a:", - error: 'Unexpected `<` in type filter', + error: 'Unexpected `<` in type filter (before `:`)', }, { query: "a<>:", @@ -303,7 +348,7 @@ const PARSED = [ original: "a<>:", returned: [], userQuery: "a<>:", - error: 'Unexpected `<` in type filter', + error: 'Unexpected `<` in type filter (before `:`)', }, { query: "a,:", @@ -312,7 +357,7 @@ const PARSED = [ original: "a,:", returned: [], userQuery: "a,:", - error: 'Unexpected `,` in type filter', + error: 'Unexpected `,` in type filter (before `:`)', }, { query: " a<> :", @@ -321,7 +366,7 @@ const PARSED = [ original: "a<> :", returned: [], userQuery: "a<> :", - error: 'Unexpected `<` in type filter', + error: 'Expected `,`, `:` or `->` after `>`, found ` `', }, { query: "mod : :", @@ -330,7 +375,16 @@ const PARSED = [ original: "mod : :", returned: [], userQuery: "mod : :", - error: 'Unexpected `:`', + error: 'Unexpected `:` (expected path after type filter `mod:`)', + }, + { + query: "mod: :", + elems: [], + foundElems: 0, + original: "mod: :", + returned: [], + userQuery: "mod: :", + error: 'Unexpected `:` (expected path after type filter `mod:`)', }, { query: "a!a", diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index 6f5d66e57ba0c..3b9cc5b1bf092 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -38,7 +38,7 @@ const PARSED = [ original: "macro:foo", returned: [], userQuery: "macro:foo", - error: "Unexpected `<` in type filter", + error: "Unexpected `<` in type filter (before `:`)", }, { query: 'macro!', diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js index eb3cf45515576..726ee56c2c1ba 100644 --- a/tests/rustdoc-js-std/parser-generics.js +++ b/tests/rustdoc-js-std/parser-generics.js @@ -9,7 +9,7 @@ const PARSED = [ error: 'Unclosed `<`', }, { - query: 'p<> u8', + query: 'p<>,u8', elems: [ { name: "p", @@ -29,9 +29,9 @@ const PARSED = [ }, ], foundElems: 2, - original: "p<> u8", + original: "p<>,u8", returned: [], - userQuery: "p<> u8", + userQuery: "p<>,u8", error: null, }, { diff --git a/tests/rustdoc-js-std/parser-quote.js b/tests/rustdoc-js-std/parser-quote.js index 9d2a3620ed7a3..731673cf4633c 100644 --- a/tests/rustdoc-js-std/parser-quote.js +++ b/tests/rustdoc-js-std/parser-quote.js @@ -38,7 +38,7 @@ const PARSED = [ original: '"p" -> a', returned: [], userQuery: '"p" -> a', - error: "You cannot have more than one element if you use quotes", + error: "Cannot have more than one element if you use quotes", }, { query: '"a" -> "p"', diff --git a/tests/rustdoc-js-std/parser-separators.js b/tests/rustdoc-js-std/parser-separators.js index 69f9ac29ad3ce..00c489b51a6a2 100644 --- a/tests/rustdoc-js-std/parser-separators.js +++ b/tests/rustdoc-js-std/parser-separators.js @@ -3,6 +3,24 @@ const PARSED = [ { query: 'aaaaaa b', + elems: [ + { + name: 'aaaaaa\tb', + fullPath: ['aaaaaa', 'b'], + pathWithoutLast: ['aaaaaa'], + pathLast: 'b', + generics: [], + typeFilter: -1, + }, + ], + foundElems: 1, + original: "aaaaaa b", + returned: [], + userQuery: "aaaaaa b", + error: null, + }, + { + query: "aaaaaa, b", elems: [ { name: 'aaaaaa', @@ -22,32 +40,24 @@ const PARSED = [ }, ], foundElems: 2, - original: "aaaaaa b", + original: "aaaaaa, b", returned: [], - userQuery: "aaaaaa b", + userQuery: "aaaaaa, b", error: null, }, { query: 'a b', elems: [ { - name: 'a', - fullPath: ['a'], - pathWithoutLast: [], - pathLast: 'a', - generics: [], - typeFilter: -1, - }, - { - name: 'b', - fullPath: ['b'], - pathWithoutLast: [], + name: 'a b', + fullPath: ['a', 'b'], + pathWithoutLast: ['a'], pathLast: 'b', generics: [], typeFilter: -1, }, ], - foundElems: 2, + foundElems: 1, original: "a b", returned: [], userQuery: "a b", @@ -83,23 +93,15 @@ const PARSED = [ query: 'a\tb', elems: [ { - name: 'a', - fullPath: ['a'], - pathWithoutLast: [], - pathLast: 'a', - generics: [], - typeFilter: -1, - }, - { - name: 'b', - fullPath: ['b'], - pathWithoutLast: [], + name: 'a\tb', + fullPath: ['a', 'b'], + pathWithoutLast: ['a'], pathLast: 'b', generics: [], typeFilter: -1, }, ], - foundElems: 2, + foundElems: 1, original: "a\tb", returned: [], userQuery: "a\tb", @@ -115,16 +117,9 @@ const PARSED = [ pathLast: 'a', generics: [ { - name: 'b', - fullPath: ['b'], - pathWithoutLast: [], - pathLast: 'b', - generics: [], - }, - { - name: 'c', - fullPath: ['c'], - pathWithoutLast: [], + name: 'b c', + fullPath: ['b', 'c'], + pathWithoutLast: ['b'], pathLast: 'c', generics: [], }, @@ -181,16 +176,9 @@ const PARSED = [ pathLast: 'a', generics: [ { - name: 'b', - fullPath: ['b'], - pathWithoutLast: [], - pathLast: 'b', - generics: [], - }, - { - name: 'c', - fullPath: ['c'], - pathWithoutLast: [], + name: 'b\tc', + fullPath: ['b', 'c'], + pathWithoutLast: ['b'], pathLast: 'c', generics: [], }, diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js index f85dd19974188..c22b7870dbf88 100644 --- a/tests/rustdoc-js-std/parser-slice-array.js +++ b/tests/rustdoc-js-std/parser-slice-array.js @@ -62,7 +62,7 @@ const PARSED = [ error: null, }, { - query: '[] u8', + query: '[],u8', elems: [ { name: "[]", @@ -82,9 +82,9 @@ const PARSED = [ }, ], foundElems: 2, - original: "[] u8", + original: "[],u8", returned: [], - userQuery: "[] u8", + userQuery: "[],u8", error: null, }, { diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index 0e08eaf73c876..720ef66c16525 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -7,23 +7,14 @@ const PARSED = [ query: 'a b', elems: [ { - name: "a", - fullPath: ["a"], - pathWithoutLast: [], - pathLast: "a", - generics: [], - typeFilter: -1, - }, - { - name: "b", - fullPath: ["b"], - pathWithoutLast: [], + name: "a b", + fullPath: ["a", "b"], + pathWithoutLast: ["a"], pathLast: "b", generics: [], - typeFilter: -1, }, ], - foundElems: 2, + foundElems: 1, original: "a b", returned: [], userQuery: "a b", @@ -33,23 +24,14 @@ const PARSED = [ query: 'a b', elems: [ { - name: "a", - fullPath: ["a"], - pathWithoutLast: [], - pathLast: "a", - generics: [], - typeFilter: -1, - }, - { - name: "b", - fullPath: ["b"], - pathWithoutLast: [], + name: "a b", + fullPath: ["a", "b"], + pathWithoutLast: ["a"], pathLast: "b", generics: [], - typeFilter: -1, }, ], - foundElems: 2, + foundElems: 1, original: "a b", returned: [], userQuery: "a b", @@ -73,7 +55,6 @@ const PARSED = [ pathWithoutLast: [], pathLast: "aaa", generics: [], - typeFilter: -1, }, { name: "a", @@ -81,7 +62,6 @@ const PARSED = [ pathWithoutLast: [], pathLast: "a", generics: [], - typeFilter: -1, }, ], foundElems: 2, @@ -106,7 +86,7 @@ const PARSED = [ original: 'mod :', returned: [], userQuery: 'mod :', - error: "Unexpected `:` (expected path after type filter)", + error: "Unexpected `:` (expected path after type filter `mod:`)", }, { query: 'mod\t:', @@ -115,6 +95,6 @@ const PARSED = [ original: 'mod\t:', returned: [], userQuery: 'mod\t:', - error: "Unexpected `:` (expected path after type filter)", + error: "Unexpected `:` (expected path after type filter `mod:`)", }, ]; From 7dc684f17364952214a5018bacf6b4296109e268 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Jun 2023 16:38:44 +0200 Subject: [PATCH 3/7] Add "vec new" test --- tests/rustdoc-js-std/vec-new.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/rustdoc-js-std/vec-new.js b/tests/rustdoc-js-std/vec-new.js index 309f3543faffe..9823a417a5d17 100644 --- a/tests/rustdoc-js-std/vec-new.js +++ b/tests/rustdoc-js-std/vec-new.js @@ -1,9 +1,20 @@ -const EXPECTED = { - 'query': 'Vec::new', - 'others': [ - { 'path': 'std::vec::Vec', 'name': 'new' }, - { 'path': 'alloc::vec::Vec', 'name': 'new' }, - { 'path': 'std::vec::Vec', 'name': 'new_in' }, - { 'path': 'alloc::vec::Vec', 'name': 'new_in' }, - ], -}; +const EXPECTED = [ + { + 'query': 'Vec::new', + 'others': [ + { 'path': 'std::vec::Vec', 'name': 'new' }, + { 'path': 'alloc::vec::Vec', 'name': 'new' }, + { 'path': 'std::vec::Vec', 'name': 'new_in' }, + { 'path': 'alloc::vec::Vec', 'name': 'new_in' }, + ], + }, + { + 'query': 'Vec new', + 'others': [ + { 'path': 'std::vec::Vec', 'name': 'new' }, + { 'path': 'alloc::vec::Vec', 'name': 'new' }, + { 'path': 'std::vec::Vec', 'name': 'new_in' }, + { 'path': 'alloc::vec::Vec', 'name': 'new_in' }, + ], + }, +]; From 64417c52d6f9d2527464e0fd1cb4d9cf6b516dcd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Jun 2023 16:42:19 +0200 Subject: [PATCH 4/7] Add documentation for paths in rustdoc search --- src/doc/rustdoc/src/how-to-read-rustdoc.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index 55cce8ab57080..9deb7009cfe00 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -110,6 +110,11 @@ Function signature searches also support arrays and slices. The explicit name or array of bytes, while square brackets `[u8]` will match either one. Empty square brackets, `[]`, will match any slice regardless of what it contains. +Paths are supported as well, you can look for `Vec::new` or `Option::Some` or +even `module::module_child::another_child::struct::field`. Whitespace characters +are considered the same as `::`, so if you write `Vec new`, it will be +considered the same as `Vec::new`. + ### Shortcuts Pressing `S` while focused elsewhere on the page will move focus to the From d1a76a9f50f55f88e6dd03f9a9ba64a02bc1f985 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Jun 2023 16:45:15 +0200 Subject: [PATCH 5/7] Update rustdoc-gui test --- tests/rustdoc-gui/search-tab-change-title-fn-sig.goml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml index b3f9ae9283f30..156d8d03ca2d1 100644 --- a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml +++ b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml @@ -55,7 +55,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types" // Try with a search-by-parameter go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "usize pattern") +write: (".search-input", "usize,pattern") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... From 4f0a9124bdfb84e71bde8e9d0b79d688baf7887f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Jun 2023 10:50:16 +0200 Subject: [PATCH 6/7] Correctly display whitespace characters in search error --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index d421adb13cbf2..35abce0903cda 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2192,7 +2192,7 @@ function initSearch(rawSearchIndex) { error.forEach((value, index) => { value = value.split("<").join("<").split(">").join(">"); if (index % 2 !== 0) { - error[index] = `${value}`; + error[index] = `${value.replaceAll(" ", " ")}`; } else { error[index] = value; } From e4ee3298656b74dd6be810862b5dd4cb8d2825b8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Jun 2023 14:01:01 +0200 Subject: [PATCH 7/7] Fix eBNF and handling of whitespace characters when not in a path --- src/librustdoc/html/static/js/search.js | 13 ++- tests/rustdoc-js-std/parser-errors.js | 106 +++++++++++++++++++++++- 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 35abce0903cda..009775645f530 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -838,7 +838,13 @@ function initSearch(rawSearchIndex) { throw ["Unexpected ", c]; } else if (c === ":" && !isPathStart(parserState)) { if (parserState.typeFilter !== null) { - throw ["Unexpected ", ":"]; + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; } else if (query.elems.length === 0) { throw ["Expected type filter before ", ":"]; } else if (query.literalSearch) { @@ -853,6 +859,9 @@ function initSearch(rawSearchIndex) { query.literalSearch = false; foundStopChar = true; continue; + } else if (isWhitespace(c)) { + skipWhitespace(parserState); + continue; } if (!foundStopChar) { let extra = ""; @@ -983,7 +992,7 @@ function initSearch(rawSearchIndex) { * 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 *(COMMA) + * type-sep = *WS 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 diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 66b0e516b2b17..b32bfea5439aa 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -366,7 +366,7 @@ const PARSED = [ original: "a<> :", returned: [], userQuery: "a<> :", - error: 'Expected `,`, `:` or `->` after `>`, found ` `', + error: 'Unexpected `<` in type filter (before `:`)', }, { query: "mod : :", @@ -440,4 +440,108 @@ const PARSED = [ userQuery: "a<", error: "Unclosed `<`", }, + { + query: "p , y", + elems: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: -1, + }, + { + name: "y", + fullPath: ["y"], + pathWithoutLast: [], + pathLast: "y", + generics: [], + typeFilter: -1, + }, + ], + foundElems: 2, + original: "p , y", + returned: [], + userQuery: "p , y", + error: null, + }, + { + query: "p", + elems: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "y", + fullPath: ["y"], + pathWithoutLast: [], + pathLast: "y", + generics: [], + typeFilter: -1, + }, + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: "p", + returned: [], + userQuery: "p", + error: null, + }, + { + query: "p ,x , y", + elems: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }, + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "y", + fullPath: ["y"], + pathWithoutLast: [], + pathLast: "y", + generics: [], + typeFilter: -1, + }, + ], + foundElems: 3, + original: "p ,x , y", + returned: [], + userQuery: "p ,x , y", + error: null, + }, ];