From 5451fe7d7cfbeb817543685e8df6a50876a5a242 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 18 Mar 2023 15:36:56 -0700 Subject: [PATCH] rustdoc: implement bag semantics for function parameter search This tweak to the function signature search engine makes things so that, if a type is repeated in the search query, it'll only match if the function actually includes it that many times. --- src/librustdoc/html/static/js/search.js | 96 +++++++++++++++++------- tests/rustdoc-js/search-bag-semantics.js | 20 +++++ tests/rustdoc-js/search-bag-semantics.rs | 4 + 3 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 tests/rustdoc-js/search-bag-semantics.js create mode 100644 tests/rustdoc-js/search-bag-semantics.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c71ce2c3001..36ff20e299e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1202,28 +1202,42 @@ function initSearch(rawSearchIndex) { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter + * @param {Array} skipPositions - Do not return one of these positions. * - * @return {integer} - Returns an edit distance to the best match. If there is no - * match, returns `maxEditDistance + 1`. + * @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) { + function findArg(row, elem, typeFilter, 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)) { + if (!typePassesFilter(typeFilter, input.ty) || + skipPositions.indexOf(i) !== -1) { + i += 1; continue; } - dist = Math.min( - dist, - checkType(input, elem, parsedQuery.literalSearch, maxEditDistance) + const typeDist = checkType( + input, + elem, + parsedQuery.literalSearch, + maxEditDistance ); - if (dist === 0) { - return 0; + if (typeDist === 0) { + return {dist: 0, position: i}; } + if (typeDist < dist) { + dist = typeDist; + position = i; + } + i += 1; } } - return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; + dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist; + return {dist, position}; } /** @@ -1232,29 +1246,43 @@ function initSearch(rawSearchIndex) { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter + * @param {Array} skipPositions - Do not return one of these positions. * - * @return {integer} - Returns an edit distance to the best match. If there is no - * match, returns `maxEditDistance + 1`. + * @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) { + function checkReturned(row, elem, typeFilter, maxEditDistance, skipPositions) { let dist = maxEditDistance + 1; + let position = -1; if (row && row.type && row.type.output.length > 0) { const ret = row.type.output; + let i = 0; for (const ret_ty of ret) { - if (!typePassesFilter(typeFilter, ret_ty.ty)) { + if (!typePassesFilter(typeFilter, ret_ty.ty) || + skipPositions.indexOf(i) !== -1) { + i += 1; continue; } - dist = Math.min( - dist, - checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance) + const typeDist = checkType( + ret_ty, + elem, + parsedQuery.literalSearch, + maxEditDistance ); - if (dist === 0) { - return 0; + if (typeDist === 0) { + return {dist: 0, position: i}; } + if (typeDist < dist) { + dist = typeDist; + position = i; + } + i += 1; } } - return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; + dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist; + return {dist, position}; } function checkPath(contains, ty, maxEditDistance) { @@ -1455,13 +1483,13 @@ 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, parsedQuery.typeFilter, maxEditDistance, []); + const returned = checkReturned(row, elem, parsedQuery.typeFilter, 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, 0, maxEditDistance); - addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance); + 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)) { return; @@ -1534,12 +1562,20 @@ function initSearch(rawSearchIndex) { // If the result is too "bad", we return false and it ends this search. function checkArgs(elems, callback) { + const skipPositions = []; for (const elem of elems) { // There is more than one parameter to the query so all checks should be "exact" - const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance); + const { dist, position } = callback( + row, + elem, + NO_TYPE_FILTER, + maxEditDistance, + skipPositions + ); if (dist <= 1) { nbDist += 1; totalDist += dist; + skipPositions.push(position); } else { return false; } @@ -1597,9 +1633,17 @@ function initSearch(rawSearchIndex) { row, elem, parsedQuery.typeFilter, + maxEditDistance, + [] + ); + addIntoResults( + results_others, + row.id, + i, + -1, + in_returned.dist, maxEditDistance ); - addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance); } } } else if (parsedQuery.foundElems > 0) { diff --git a/tests/rustdoc-js/search-bag-semantics.js b/tests/rustdoc-js/search-bag-semantics.js new file mode 100644 index 00000000000..c56a3df5f90 --- /dev/null +++ b/tests/rustdoc-js/search-bag-semantics.js @@ -0,0 +1,20 @@ +// exact-check + +const QUERY = [ + 'P', + 'P, P', +]; + +const EXPECTED = [ + { + 'in_args': [ + { 'path': 'search_bag_semantics', 'name': 'alacazam' }, + { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, + ], + }, + { + 'others': [ + { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, + ], + }, +]; diff --git a/tests/rustdoc-js/search-bag-semantics.rs b/tests/rustdoc-js/search-bag-semantics.rs new file mode 100644 index 00000000000..546572dc4ef --- /dev/null +++ b/tests/rustdoc-js/search-bag-semantics.rs @@ -0,0 +1,4 @@ +pub struct P; + +pub fn abracadabra(a: P, b: P) {} +pub fn alacazam(a: P) {}