From e71d2ba3bc14152d9978c6d158c466426382d19a Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 15 Dec 2021 19:44:26 -0800 Subject: [PATCH] search.js: split out nested functions `execSearch` was a giant function with a number of other very large functions embedded inside it. This moves the embedded functions out of execSearch, so execSearch's function signature can be read next to its code. `sortResults` was relying on capturing certain variables from its environment, so I made those into explicity parameters passed into `sortResults`. --- src/librustdoc/html/static/js/search.js | 1091 ++++++++++++----------- 1 file changed, 546 insertions(+), 545 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7c55d10836c45..991e7ef15da39 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -151,467 +151,6 @@ window.initSearch = function(rawSearchIndex) { removeEmptyStringsFromArray(split); - function transformResults(results) { - var out = []; - for (var i = 0, len = results.length; i < len; ++i) { - if (results[i].id > -1) { - var obj = searchIndex[results[i].id]; - obj.lev = results[i].lev; - var res = buildHrefAndPath(obj); - obj.displayPath = pathSplitter(res[0]); - obj.fullPath = obj.displayPath + obj.name; - // To be sure than it some items aren't considered as duplicate. - obj.fullPath += "|" + obj.ty; - obj.href = res[1]; - out.push(obj); - if (out.length >= MAX_RESULTS) { - break; - } - } - } - return out; - } - - function sortResults(results, isType) { - var ar = []; - for (var entry in results) { - if (hasOwnPropertyRustdoc(results, entry)) { - ar.push(results[entry]); - } - } - results = ar; - var i, len, result; - for (i = 0, len = results.length; i < len; ++i) { - result = results[i]; - result.word = searchWords[result.id]; - result.item = searchIndex[result.id] || {}; - } - // if there are no results then return to default and fail - if (results.length === 0) { - return []; - } - - results.sort(function(aaa, bbb) { - var a, b; - - // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== val); - b = (bbb.word !== val); - if (a !== b) { return a - b; } - - // Sort by non levenshtein results and then levenshtein results by the distance - // (less changes required to match means higher rankings) - a = (aaa.lev); - b = (bbb.lev); - if (a !== b) { return a - b; } - - // sort by crate (non-current crate goes later) - a = (aaa.item.crate !== window.currentCrate); - b = (bbb.item.crate !== window.currentCrate); - if (a !== b) { return a - b; } - - // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; - if (a !== b) { return a - b; } - - // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { return (a > b ? +1 : -1); } - - // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); - if (a !== b) { return a - b; } - // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; - if (a !== b) { return a - b; } - - // special precedence for primitive and keyword pages - if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || - (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { - return -1; - } - if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || - (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { - return 1; - } - - // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); - if (a !== b) { return a - b; } - - // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; - if (a !== b) { return a - b; } - - // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { return (a > b ? +1 : -1); } - - // que sera, sera - return 0; - }); - - for (i = 0, len = results.length; i < len; ++i) { - result = results[i]; - - // this validation does not make sense when searching by types - if (result.dontValidate) { - continue; - } - var name = result.item.name.toLowerCase(), - path = result.item.path.toLowerCase(), - parent = result.item.parent; - - if (!isType && !validateResult(name, path, split, parent)) { - result.id = -1; - } - } - return transformResults(results); - } - - function extractGenerics(val) { - val = val.toLowerCase(); - if (val.indexOf("<") !== -1) { - var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); - return { - name: val.substring(0, val.indexOf("<")), - generics: values.split(/\s*,\s*/), - }; - } - return { - name: val, - generics: [], - }; - } - - function checkGenerics(obj, val) { - // The names match, but we need to be sure that all generics kinda - // match as well. - var tmp_lev, elem_name; - if (val.generics.length > 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length >= val.generics.length) { - var elems = Object.create(null); - var elength = obj[GENERICS_DATA].length; - for (var x = 0; x < elength; ++x) { - if (!elems[obj[GENERICS_DATA][x][NAME]]) { - elems[obj[GENERICS_DATA][x][NAME]] = 0; - } - elems[obj[GENERICS_DATA][x][NAME]] += 1; - } - var total = 0; - var done = 0; - // We need to find the type that matches the most to remove it in order - // to move forward. - var vlength = val.generics.length; - for (x = 0; x < vlength; ++x) { - var lev = MAX_LEV_DISTANCE + 1; - var firstGeneric = val.generics[x]; - var match = null; - if (elems[firstGeneric]) { - match = firstGeneric; - lev = 0; - } else { - for (elem_name in elems) { - tmp_lev = levenshtein(elem_name, firstGeneric); - if (tmp_lev < lev) { - lev = tmp_lev; - match = elem_name; - } - } - } - if (match !== null) { - elems[match] -= 1; - if (elems[match] == 0) { - delete elems[match]; - } - total += lev; - done += 1; - } else { - return MAX_LEV_DISTANCE + 1; - } - } - return Math.ceil(total / done); - } - } - return MAX_LEV_DISTANCE + 1; - } - - // Check for type name and type generics (if any). - function checkType(obj, val, literalSearch) { - var lev_distance = MAX_LEV_DISTANCE + 1; - var tmp_lev = MAX_LEV_DISTANCE + 1; - var len, x, firstGeneric; - if (obj[NAME] === val.name) { - if (literalSearch) { - if (val.generics && val.generics.length !== 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length > 0) { - var elems = Object.create(null); - len = obj[GENERICS_DATA].length; - for (x = 0; x < len; ++x) { - if (!elems[obj[GENERICS_DATA][x][NAME]]) { - elems[obj[GENERICS_DATA][x][NAME]] = 0; - } - elems[obj[GENERICS_DATA][x][NAME]] += 1; - } - - var allFound = true; - len = val.generics.length; - for (x = 0; x < len; ++x) { - firstGeneric = val.generics[x]; - if (elems[firstGeneric]) { - elems[firstGeneric] -= 1; - } else { - allFound = false; - break; - } - } - if (allFound) { - return true; - } - } - return false; - } - return true; - } else { - // If the type has generics but don't match, then it won't return at this point. - // Otherwise, `checkGenerics` will return 0 and it'll return. - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { - tmp_lev = checkGenerics(obj, val); - if (tmp_lev <= MAX_LEV_DISTANCE) { - return tmp_lev; - } - } - } - } else if (literalSearch) { - if ((!val.generics || val.generics.length === 0) && - obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - return obj[GENERICS_DATA].some( - function(gen) { - return gen[NAME] === val.name; - }); - } - return false; - } - lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); - if (lev_distance <= MAX_LEV_DISTANCE) { - // The generics didn't match but the name kinda did so we give it - // a levenshtein distance value that isn't *this* good so it goes - // into the search results but not too high. - lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); - } - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - // We can check if the type we're looking for is inside the generics! - var olength = obj[GENERICS_DATA].length; - for (x = 0; x < olength; ++x) { - tmp_lev = Math.min(levenshtein(obj[GENERICS_DATA][x][NAME], val.name), tmp_lev); - } - if (tmp_lev !== 0) { - // If we didn't find a good enough result, we go check inside the generics of - // the generics. - for (x = 0; x < olength && tmp_lev !== 0; ++x) { - tmp_lev = Math.min( - checkType(obj[GENERICS_DATA][x], val, literalSearch), - tmp_lev - ); - } - } - } - // Now whatever happens, the returned distance is "less good" so we should mark it - // as such, and so we add 1 to the distance to make it "less good". - return Math.min(lev_distance, tmp_lev) + 1; - } - - function findArg(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { - var length = obj.type[INPUTS_DATA].length; - for (var i = 0; i < length; i++) { - var tmp = obj.type[INPUTS_DATA][i]; - if (!typePassesFilter(typeFilter, tmp[1])) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch) { - if (tmp) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch ? false : lev_distance; - } - - function checkReturned(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type.length > OUTPUT_DATA) { - var ret = obj.type[OUTPUT_DATA]; - if (typeof ret[0] === "string") { - ret = [ret]; - } - for (var x = 0, len = ret.length; x < len; ++x) { - var tmp = ret[x]; - if (!typePassesFilter(typeFilter, tmp[1])) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch) { - if (tmp) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch ? false : lev_distance; - } - - function checkPath(contains, lastElem, ty) { - if (contains.length === 0) { - return 0; - } - var ret_lev = MAX_LEV_DISTANCE + 1; - var path = ty.path.split("::"); - - if (ty.parent && ty.parent.name) { - path.push(ty.parent.name.toLowerCase()); - } - - var length = path.length; - var clength = contains.length; - if (clength > length) { - return MAX_LEV_DISTANCE + 1; - } - for (var i = 0; i < length; ++i) { - if (i + clength > length) { - break; - } - var lev_total = 0; - var aborted = false; - for (var x = 0; x < clength; ++x) { - var lev = levenshtein(path[i + x], contains[x]); - if (lev > MAX_LEV_DISTANCE) { - aborted = true; - break; - } - lev_total += lev; - } - if (!aborted) { - ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); - } - } - return ret_lev; - } - - function typePassesFilter(filter, type) { - // No filter - if (filter <= NO_TYPE_FILTER) return true; - - // Exact match - if (filter === type) return true; - - // Match related items - var name = itemTypes[type]; - switch (itemTypes[filter]) { - case "constant": - return name === "associatedconstant"; - case "fn": - return name === "method" || name === "tymethod"; - case "type": - return name === "primitive" || name === "associatedtype"; - case "trait": - return name === "traitalias"; - } - - // No match - return false; - } - - function createAliasFromItem(item) { - return { - crate: item.crate, - name: item.name, - path: item.path, - desc: item.desc, - ty: item.ty, - parent: item.parent, - type: item.type, - is_alias: true, - }; - } - - function handleAliases(ret, query, filterCrates) { - // We separate aliases and crate aliases because we want to have current crate - // aliases to be before the others in the displayed results. - var aliases = []; - var crateAliases = []; - if (filterCrates !== undefined) { - if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { - var query_aliases = ALIASES[filterCrates][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - } else { - Object.keys(ALIASES).forEach(function(crate) { - if (ALIASES[crate][query.search]) { - var pushTo = crate === window.currentCrate ? crateAliases : aliases; - var query_aliases = ALIASES[crate][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - }); - } - - var sortFunc = function(aaa, bbb) { - if (aaa.path < bbb.path) { - return 1; - } else if (aaa.path === bbb.path) { - return 0; - } - return -1; - }; - crateAliases.sort(sortFunc); - aliases.sort(sortFunc); - - var pushFunc = function(alias) { - alias.alias = query.raw; - var res = buildHrefAndPath(alias); - alias.displayPath = pathSplitter(res[0]); - alias.fullPath = alias.displayPath + alias.name; - alias.href = res[1]; - - ret.others.unshift(alias); - if (ret.others.length > MAX_RESULTS) { - ret.others.pop(); - } - }; - onEach(aliases, pushFunc); - onEach(crateAliases, pushFunc); - } - // quoted values mean literal search var nSearchWords = searchWords.length; var i, it; @@ -828,14 +367,476 @@ window.initSearch = function(rawSearchIndex) { } var ret = { - "in_args": sortResults(results_in_args, true), - "returned": sortResults(results_returned, true), - "others": sortResults(results, false), + "in_args": sortResults(results_in_args, true, searchWords, searchIndex, val, split), + "returned": sortResults(results_returned, true, searchWords, searchIndex, val, split), + "others": sortResults(results, false, searchWords, searchIndex, val, split), }; handleAliases(ret, query, filterCrates); return ret; } + function transformResults(results) { + var out = []; + for (var i = 0, len = results.length; i < len; ++i) { + if (results[i].id > -1) { + var obj = searchIndex[results[i].id]; + obj.lev = results[i].lev; + var res = buildHrefAndPath(obj); + obj.displayPath = pathSplitter(res[0]); + obj.fullPath = obj.displayPath + obj.name; + // To be sure than it some items aren't considered as duplicate. + obj.fullPath += "|" + obj.ty; + obj.href = res[1]; + out.push(obj); + if (out.length >= MAX_RESULTS) { + break; + } + } + } + return out; + } + + function sortResults(results, isType, searchWords, searchIndex, val, split) { + var ar = []; + for (var entry in results) { + if (hasOwnPropertyRustdoc(results, entry)) { + ar.push(results[entry]); + } + } + results = ar; + var i, len, result; + for (i = 0, len = results.length; i < len; ++i) { + result = results[i]; + result.word = searchWords[result.id]; + result.item = searchIndex[result.id] || {}; + } + // if there are no results then return to default and fail + if (results.length === 0) { + return []; + } + + results.sort(function(aaa, bbb) { + var a, b; + + // sort by exact match with regard to the last word (mismatch goes later) + a = (aaa.word !== val); + b = (bbb.word !== val); + if (a !== b) { return a - b; } + + // Sort by non levenshtein results and then levenshtein results by the distance + // (less changes required to match means higher rankings) + a = (aaa.lev); + b = (bbb.lev); + if (a !== b) { return a - b; } + + // sort by crate (non-current crate goes later) + a = (aaa.item.crate !== window.currentCrate); + b = (bbb.item.crate !== window.currentCrate); + if (a !== b) { return a - b; } + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) { return a - b; } + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) { return (a > b ? +1 : -1); } + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { return a - b; } + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { return a - b; } + + // special precedence for primitive and keyword pages + if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || + (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { + return -1; + } + if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || + (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { + return 1; + } + + // sort by description (no description goes later) + a = (aaa.item.desc === ""); + b = (bbb.item.desc === ""); + if (a !== b) { return a - b; } + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) { return a - b; } + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) { return (a > b ? +1 : -1); } + + // que sera, sera + return 0; + }); + + for (i = 0, len = results.length; i < len; ++i) { + result = results[i]; + + // this validation does not make sense when searching by types + if (result.dontValidate) { + continue; + } + var name = result.item.name.toLowerCase(), + path = result.item.path.toLowerCase(), + parent = result.item.parent; + + if (!isType && !validateResult(name, path, split, parent)) { + result.id = -1; + } + } + return transformResults(results); + } + + function extractGenerics(val) { + val = val.toLowerCase(); + if (val.indexOf("<") !== -1) { + var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); + return { + name: val.substring(0, val.indexOf("<")), + generics: values.split(/\s*,\s*/), + }; + } + return { + name: val, + generics: [], + }; + } + + function checkGenerics(obj, val) { + // The names match, but we need to be sure that all generics kinda + // match as well. + var tmp_lev, elem_name; + if (val.generics.length > 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length >= val.generics.length) { + var elems = Object.create(null); + var elength = obj[GENERICS_DATA].length; + for (var x = 0; x < elength; ++x) { + if (!elems[obj[GENERICS_DATA][x][NAME]]) { + elems[obj[GENERICS_DATA][x][NAME]] = 0; + } + elems[obj[GENERICS_DATA][x][NAME]] += 1; + } + var total = 0; + var done = 0; + // We need to find the type that matches the most to remove it in order + // to move forward. + var vlength = val.generics.length; + for (x = 0; x < vlength; ++x) { + var lev = MAX_LEV_DISTANCE + 1; + var firstGeneric = val.generics[x]; + var match = null; + if (elems[firstGeneric]) { + match = firstGeneric; + lev = 0; + } else { + for (elem_name in elems) { + tmp_lev = levenshtein(elem_name, firstGeneric); + if (tmp_lev < lev) { + lev = tmp_lev; + match = elem_name; + } + } + } + if (match !== null) { + elems[match] -= 1; + if (elems[match] == 0) { + delete elems[match]; + } + total += lev; + done += 1; + } else { + return MAX_LEV_DISTANCE + 1; + } + } + return Math.ceil(total / done); + } + } + return MAX_LEV_DISTANCE + 1; + } + + // Check for type name and type generics (if any). + function checkType(obj, val, literalSearch) { + var lev_distance = MAX_LEV_DISTANCE + 1; + var tmp_lev = MAX_LEV_DISTANCE + 1; + var len, x, firstGeneric; + if (obj[NAME] === val.name) { + if (literalSearch) { + if (val.generics && val.generics.length !== 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length > 0) { + var elems = Object.create(null); + len = obj[GENERICS_DATA].length; + for (x = 0; x < len; ++x) { + if (!elems[obj[GENERICS_DATA][x][NAME]]) { + elems[obj[GENERICS_DATA][x][NAME]] = 0; + } + elems[obj[GENERICS_DATA][x][NAME]] += 1; + } + + var allFound = true; + len = val.generics.length; + for (x = 0; x < len; ++x) { + firstGeneric = val.generics[x]; + if (elems[firstGeneric]) { + elems[firstGeneric] -= 1; + } else { + allFound = false; + break; + } + } + if (allFound) { + return true; + } + } + return false; + } + return true; + } else { + // If the type has generics but don't match, then it won't return at this point. + // Otherwise, `checkGenerics` will return 0 and it'll return. + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { + tmp_lev = checkGenerics(obj, val); + if (tmp_lev <= MAX_LEV_DISTANCE) { + return tmp_lev; + } + } + } + } else if (literalSearch) { + if ((!val.generics || val.generics.length === 0) && + obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + return obj[GENERICS_DATA].some( + function(gen) { + return gen[NAME] === val.name; + }); + } + return false; + } + lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); + if (lev_distance <= MAX_LEV_DISTANCE) { + // The generics didn't match but the name kinda did so we give it + // a levenshtein distance value that isn't *this* good so it goes + // into the search results but not too high. + lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); + } + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + // We can check if the type we're looking for is inside the generics! + var olength = obj[GENERICS_DATA].length; + for (x = 0; x < olength; ++x) { + tmp_lev = Math.min(levenshtein(obj[GENERICS_DATA][x][NAME], val.name), tmp_lev); + } + if (tmp_lev !== 0) { + // If we didn't find a good enough result, we go check inside the generics of + // the generics. + for (x = 0; x < olength && tmp_lev !== 0; ++x) { + tmp_lev = Math.min( + checkType(obj[GENERICS_DATA][x], val, literalSearch), + tmp_lev + ); + } + } + } + // Now whatever happens, the returned distance is "less good" so we should mark it + // as such, and so we add 1 to the distance to make it "less good". + return Math.min(lev_distance, tmp_lev) + 1; + } + + function findArg(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { + var length = obj.type[INPUTS_DATA].length; + for (var i = 0; i < length; i++) { + var tmp = obj.type[INPUTS_DATA][i]; + if (!typePassesFilter(typeFilter, tmp[1])) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch) { + if (tmp) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch ? false : lev_distance; + } + + function checkReturned(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type.length > OUTPUT_DATA) { + var ret = obj.type[OUTPUT_DATA]; + if (typeof ret[0] === "string") { + ret = [ret]; + } + for (var x = 0, len = ret.length; x < len; ++x) { + var tmp = ret[x]; + if (!typePassesFilter(typeFilter, tmp[1])) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch) { + if (tmp) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch ? false : lev_distance; + } + + function checkPath(contains, lastElem, ty) { + if (contains.length === 0) { + return 0; + } + var ret_lev = MAX_LEV_DISTANCE + 1; + var path = ty.path.split("::"); + + if (ty.parent && ty.parent.name) { + path.push(ty.parent.name.toLowerCase()); + } + + var length = path.length; + var clength = contains.length; + if (clength > length) { + return MAX_LEV_DISTANCE + 1; + } + for (var i = 0; i < length; ++i) { + if (i + clength > length) { + break; + } + var lev_total = 0; + var aborted = false; + for (var x = 0; x < clength; ++x) { + var lev = levenshtein(path[i + x], contains[x]); + if (lev > MAX_LEV_DISTANCE) { + aborted = true; + break; + } + lev_total += lev; + } + if (!aborted) { + ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + } + } + return ret_lev; + } + + function typePassesFilter(filter, type) { + // No filter + if (filter <= NO_TYPE_FILTER) return true; + + // Exact match + if (filter === type) return true; + + // Match related items + var name = itemTypes[type]; + switch (itemTypes[filter]) { + case "constant": + return name === "associatedconstant"; + case "fn": + return name === "method" || name === "tymethod"; + case "type": + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; + } + + // No match + return false; + } + + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + desc: item.desc, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + }; + } + + function handleAliases(ret, query, filterCrates) { + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + var aliases = []; + var crateAliases = []; + if (filterCrates !== undefined) { + if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { + var query_aliases = ALIASES[filterCrates][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + } else { + Object.keys(ALIASES).forEach(function(crate) { + if (ALIASES[crate][query.search]) { + var pushTo = crate === window.currentCrate ? crateAliases : aliases; + var query_aliases = ALIASES[crate][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + }); + } + + var sortFunc = function(aaa, bbb) { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); + + var pushFunc = function(alias) { + alias.alias = query.raw; + var res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; + + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; + onEach(aliases, pushFunc); + onEach(crateAliases, pushFunc); + } + + /** * Validate performs the following boolean logic. For example: * "File::open" will give IF A PARENT EXISTS => ("file" && "open") @@ -1136,87 +1137,6 @@ window.initSearch = function(rawSearchIndex) { } function execSearch(query, searchWords, filterCrates) { - function getSmallest(arrays, positions, notDuplicates) { - var start = null; - - for (var it = 0, len = positions.length; it < len; ++it) { - if (arrays[it].length > positions[it] && - (start === null || start > arrays[it][positions[it]].lev) && - !notDuplicates[arrays[it][positions[it]].fullPath]) { - start = arrays[it][positions[it]].lev; - } - } - return start; - } - - function mergeArrays(arrays) { - var ret = []; - var positions = []; - var notDuplicates = {}; - - for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { - positions.push(0); - } - while (ret.length < MAX_RESULTS) { - var smallest = getSmallest(arrays, positions, notDuplicates); - - if (smallest === null) { - break; - } - for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { - if (arrays[x].length > positions[x] && - arrays[x][positions[x]].lev === smallest && - !notDuplicates[arrays[x][positions[x]].fullPath]) { - ret.push(arrays[x][positions[x]]); - notDuplicates[arrays[x][positions[x]].fullPath] = true; - positions[x] += 1; - } - } - } - return ret; - } - - // Split search query by ",", while respecting angle bracket nesting. - // Since "<" is an alias for the Ord family of traits, it also uses - // lookahead to distinguish "<"-as-less-than from "<"-as-angle-bracket. - // - // tokenizeQuery("A, D") == ["A", "D"] - // tokenizeQuery("A)/g; - var ret = []; - var start = 0; - for (i = 0; i < l; ++i) { - switch (raw[i]) { - case "<": - nextAngle.lastIndex = i + 1; - matched = nextAngle.exec(raw); - if (matched && matched[1] === '>') { - depth += 1; - } - break; - case ">": - if (depth > 0) { - depth -= 1; - } - break; - case ",": - if (depth === 0) { - ret.push(raw.substring(start, i)); - start = i + 1; - } - break; - } - } - if (start !== i) { - ret.push(raw.substring(start, i)); - } - return ret; - } - var queries = tokenizeQuery(query.raw); var results = { "in_args": [], @@ -1248,6 +1168,87 @@ window.initSearch = function(rawSearchIndex) { }; } + function getSmallest(arrays, positions, notDuplicates) { + var start = null; + + for (var it = 0, len = positions.length; it < len; ++it) { + if (arrays[it].length > positions[it] && + (start === null || start > arrays[it][positions[it]].lev) && + !notDuplicates[arrays[it][positions[it]].fullPath]) { + start = arrays[it][positions[it]].lev; + } + } + return start; + } + + function mergeArrays(arrays) { + var ret = []; + var positions = []; + var notDuplicates = {}; + + for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { + positions.push(0); + } + while (ret.length < MAX_RESULTS) { + var smallest = getSmallest(arrays, positions, notDuplicates); + + if (smallest === null) { + break; + } + for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { + if (arrays[x].length > positions[x] && + arrays[x][positions[x]].lev === smallest && + !notDuplicates[arrays[x][positions[x]].fullPath]) { + ret.push(arrays[x][positions[x]]); + notDuplicates[arrays[x][positions[x]].fullPath] = true; + positions[x] += 1; + } + } + } + return ret; + } + + // Split search query by ",", while respecting angle bracket nesting. + // Since "<" is an alias for the Ord family of traits, it also uses + // lookahead to distinguish "<"-as-less-than from "<"-as-angle-bracket. + // + // tokenizeQuery("A, D") == ["A", "D"] + // tokenizeQuery("A)/g; + var ret = []; + var start = 0; + for (i = 0; i < l; ++i) { + switch (raw[i]) { + case "<": + nextAngle.lastIndex = i + 1; + matched = nextAngle.exec(raw); + if (matched && matched[1] === '>') { + depth += 1; + } + break; + case ">": + if (depth > 0) { + depth -= 1; + } + break; + case ",": + if (depth === 0) { + ret.push(raw.substring(start, i)); + start = i + 1; + } + break; + } + } + if (start !== i) { + ret.push(raw.substring(start, i)); + } + return ret; + } + function getFilterCrates() { var elem = document.getElementById("crate-search");