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.
This commit is contained in:
Michael Howell 2023-03-18 15:36:56 -07:00
parent 8826b68c62
commit 5451fe7d7c
3 changed files with 94 additions and 26 deletions

View File

@ -1202,28 +1202,42 @@ function initSearch(rawSearchIndex) {
* @param {Row} row * @param {Row} row
* @param {QueryElement} elem - The element from the parsed query. * @param {QueryElement} elem - The element from the parsed query.
* @param {integer} typeFilter * @param {integer} typeFilter
* @param {Array<integer>} skipPositions - Do not return one of these positions.
* *
* @return {integer} - Returns an edit distance to the best match. If there is no * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
* match, returns `maxEditDistance + 1`. * 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 dist = maxEditDistance + 1;
let position = -1;
if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
let i = 0;
for (const input of row.type.inputs) { for (const input of row.type.inputs) {
if (!typePassesFilter(typeFilter, input.ty)) { if (!typePassesFilter(typeFilter, input.ty) ||
skipPositions.indexOf(i) !== -1) {
i += 1;
continue; continue;
} }
dist = Math.min( const typeDist = checkType(
dist, input,
checkType(input, elem, parsedQuery.literalSearch, maxEditDistance) elem,
parsedQuery.literalSearch,
maxEditDistance
); );
if (dist === 0) { if (typeDist === 0) {
return 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 {Row} row
* @param {QueryElement} elem - The element from the parsed query. * @param {QueryElement} elem - The element from the parsed query.
* @param {integer} typeFilter * @param {integer} typeFilter
* @param {Array<integer>} skipPositions - Do not return one of these positions.
* *
* @return {integer} - Returns an edit distance to the best match. If there is no * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
* match, returns `maxEditDistance + 1`. * 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 dist = maxEditDistance + 1;
let position = -1;
if (row && row.type && row.type.output.length > 0) { if (row && row.type && row.type.output.length > 0) {
const ret = row.type.output; const ret = row.type.output;
let i = 0;
for (const ret_ty of ret) { 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; continue;
} }
dist = Math.min( const typeDist = checkType(
dist, ret_ty,
checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance) elem,
parsedQuery.literalSearch,
maxEditDistance
); );
if (dist === 0) { if (typeDist === 0) {
return 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) { function checkPath(contains, ty, maxEditDistance) {
@ -1455,13 +1483,13 @@ function initSearch(rawSearchIndex) {
const fullId = row.id; const fullId = row.id;
const searchWord = searchWords[pos]; const searchWord = searchWords[pos];
const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance); const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance, []);
const returned = checkReturned(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 // path_dist is 0 because no parent path information is currently stored
// in the search index // in the search index
addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance); addIntoResults(results_in_args, fullId, pos, -1, in_args.dist, 0, maxEditDistance);
addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance); addIntoResults(results_returned, fullId, pos, -1, returned.dist, 0, maxEditDistance);
if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
return; return;
@ -1534,12 +1562,20 @@ function initSearch(rawSearchIndex) {
// If the result is too "bad", we return false and it ends this search. // If the result is too "bad", we return false and it ends this search.
function checkArgs(elems, callback) { function checkArgs(elems, callback) {
const skipPositions = [];
for (const elem of elems) { for (const elem of elems) {
// There is more than one parameter to the query so all checks should be "exact" // 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) { if (dist <= 1) {
nbDist += 1; nbDist += 1;
totalDist += dist; totalDist += dist;
skipPositions.push(position);
} else { } else {
return false; return false;
} }
@ -1597,9 +1633,17 @@ function initSearch(rawSearchIndex) {
row, row,
elem, elem,
parsedQuery.typeFilter, parsedQuery.typeFilter,
maxEditDistance,
[]
);
addIntoResults(
results_others,
row.id,
i,
-1,
in_returned.dist,
maxEditDistance maxEditDistance
); );
addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance);
} }
} }
} else if (parsedQuery.foundElems > 0) { } else if (parsedQuery.foundElems > 0) {

View File

@ -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' },
],
},
];

View File

@ -0,0 +1,4 @@
pub struct P;
pub fn abracadabra(a: P, b: P) {}
pub fn alacazam(a: P) {}