Skip to content

Commit 6b69ebc

Browse files
committed
rustdoc-search: remove parallel searchWords array
This might have made sense if the algorithm could use `searchWords` to skip having to look at `searchIndex`, but since it always does a substring check on both the stock word and the normalizedName, it doesn't seem to help performance anyway.
1 parent e6707df commit 6b69ebc

File tree

4 files changed

+48
-56
lines changed

4 files changed

+48
-56
lines changed

src/librustdoc/html/static/js/search.js

+28-46
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ function initSearch(rawSearchIndex) {
499499
fullPath: ["never"],
500500
pathWithoutLast: [],
501501
pathLast: "never",
502+
normalizedPathLast: "never",
502503
generics: [],
503504
bindings: new Map(),
504505
typeFilter: "primitive",
@@ -537,12 +538,14 @@ function initSearch(rawSearchIndex) {
537538
const bindingName = parserState.isInBinding;
538539
parserState.isInBinding = null;
539540
const bindings = new Map();
541+
const pathLast = pathSegments[pathSegments.length - 1];
540542
return {
541543
name: name.trim(),
542544
id: null,
543545
fullPath: pathSegments,
544546
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
545-
pathLast: pathSegments[pathSegments.length - 1],
547+
pathLast,
548+
normalizedPathLast: pathLast.replace(/_/g, ""),
546549
generics: generics.filter(gen => {
547550
// Syntactically, bindings are parsed as generics,
548551
// but the query engine treats them differently.
@@ -689,6 +692,7 @@ function initSearch(rawSearchIndex) {
689692
fullPath: ["[]"],
690693
pathWithoutLast: [],
691694
pathLast: "[]",
695+
normalizedPathLast: "[]",
692696
generics,
693697
typeFilter: "primitive",
694698
bindingName: isInBinding,
@@ -1168,13 +1172,12 @@ function initSearch(rawSearchIndex) {
11681172
* Executes the parsed query and builds a {ResultsTable}.
11691173
*
11701174
* @param {ParsedQuery} parsedQuery - The parsed user query
1171-
* @param {Object} searchWords - The list of search words to query against
11721175
* @param {Object} [filterCrates] - Crate to search in if defined
11731176
* @param {Object} [currentCrate] - Current crate, to rank results from this crate higher
11741177
*
11751178
* @return {ResultsTable}
11761179
*/
1177-
function execQuery(parsedQuery, searchWords, filterCrates, currentCrate) {
1180+
function execQuery(parsedQuery, filterCrates, currentCrate) {
11781181
const results_others = new Map(), results_in_args = new Map(),
11791182
results_returned = new Map();
11801183

@@ -1232,8 +1235,8 @@ function initSearch(rawSearchIndex) {
12321235
const userQuery = parsedQuery.userQuery;
12331236
const result_list = [];
12341237
for (const result of results.values()) {
1235-
result.word = searchWords[result.id];
1236-
result.item = searchIndex[result.id] || {};
1238+
result.item = searchIndex[result.id];
1239+
result.word = searchIndex[result.id].word;
12371240
result_list.push(result);
12381241
}
12391242

@@ -1928,7 +1931,7 @@ function initSearch(rawSearchIndex) {
19281931
* The `results` map contains information which will be used to sort the search results:
19291932
*
19301933
* * `fullId` is a `string`` used as the key of the object we use for the `results` map.
1931-
* * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
1934+
* * `id` is the index in the `searchIndex` array for this element.
19321935
* * `index` is an `integer`` used to sort by the position of the word in the item's name.
19331936
* * `dist` is the main metric used to sort the search results.
19341937
* * `path_dist` is zero if a single-component search query is used, otherwise it's the
@@ -1986,9 +1989,8 @@ function initSearch(rawSearchIndex) {
19861989
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
19871990
return;
19881991
}
1989-
let index = -1, path_dist = 0;
1992+
let path_dist = 0;
19901993
const fullId = row.id;
1991-
const searchWord = searchWords[pos];
19921994

19931995
// fpDist is a minimum possible type distance, where "type distance" is the number of
19941996
// atoms in the function not present in the query
@@ -2021,19 +2023,10 @@ function initSearch(rawSearchIndex) {
20212023
return;
20222024
}
20232025

2024-
const row_index = row.normalizedName.indexOf(elem.pathLast);
2025-
const word_index = searchWord.indexOf(elem.pathLast);
2026-
2027-
// lower indexes are "better" matches
2028-
// rank based on the "best" match
2029-
if (row_index === -1) {
2030-
index = word_index;
2031-
} else if (word_index === -1) {
2032-
index = row_index;
2033-
} else if (word_index < row_index) {
2034-
index = word_index;
2035-
} else {
2036-
index = row_index;
2026+
let index = row.word.indexOf(elem.pathLast);
2027+
const normalizedIndex = row.normalizedName.indexOf(elem.pathLast);
2028+
if (index === -1 || (index > normalizedIndex && normalizedIndex !== -1)) {
2029+
index = normalizedIndex;
20372030
}
20382031

20392032
if (elem.fullPath.length > 1) {
@@ -2044,13 +2037,13 @@ function initSearch(rawSearchIndex) {
20442037
}
20452038

20462039
if (parsedQuery.literalSearch) {
2047-
if (searchWord === elem.name) {
2040+
if (row.word === elem.pathLast) {
20482041
addIntoResults(results_others, fullId, pos, index, 0, path_dist);
20492042
}
20502043
return;
20512044
}
20522045

2053-
const dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
2046+
const dist = editDistance(row.normalizedName, elem.normalizedPathLast, maxEditDistance);
20542047

20552048
if (index === -1 && dist + path_dist > maxEditDistance) {
20562049
return;
@@ -2135,15 +2128,15 @@ function initSearch(rawSearchIndex) {
21352128
* @param {boolean} isAssocType
21362129
*/
21372130
function convertNameToId(elem, isAssocType) {
2138-
if (typeNameIdMap.has(elem.pathLast) &&
2139-
(isAssocType || !typeNameIdMap.get(elem.pathLast).assocOnly)) {
2140-
elem.id = typeNameIdMap.get(elem.pathLast).id;
2131+
if (typeNameIdMap.has(elem.normalizedPathLast) &&
2132+
(isAssocType || !typeNameIdMap.get(elem.normalizedPathLast).assocOnly)) {
2133+
elem.id = typeNameIdMap.get(elem.normalizedPathLast).id;
21412134
} else if (!parsedQuery.literalSearch) {
21422135
let match = null;
21432136
let matchDist = maxEditDistance + 1;
21442137
let matchName = "";
21452138
for (const [name, {id, assocOnly}] of typeNameIdMap) {
2146-
const dist = editDistance(name, elem.pathLast, maxEditDistance);
2139+
const dist = editDistance(name, elem.normalizedPathLast, maxEditDistance);
21472140
if (dist <= matchDist && dist <= maxEditDistance &&
21482141
(isAssocType || !assocOnly)) {
21492142
if (dist === matchDist && matchName > name) {
@@ -2236,7 +2229,7 @@ function initSearch(rawSearchIndex) {
22362229
if (parsedQuery.foundElems === 1 && parsedQuery.returned.length === 0) {
22372230
if (parsedQuery.elems.length === 1) {
22382231
const elem = parsedQuery.elems[0];
2239-
for (let i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
2232+
for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) {
22402233
// It means we want to check for this element everywhere (in names, args and
22412234
// returned).
22422235
handleSingleArg(
@@ -2267,7 +2260,7 @@ function initSearch(rawSearchIndex) {
22672260
};
22682261
parsedQuery.elems.sort(sortQ);
22692262
parsedQuery.returned.sort(sortQ);
2270-
for (let i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
2263+
for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) {
22712264
handleArgs(searchIndex[i], i, results_others);
22722265
}
22732266
}
@@ -2651,7 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
26512644
updateSearchHistory(buildUrl(query.original, filterCrates));
26522645

26532646
showResults(
2654-
execQuery(query, searchWords, filterCrates, window.currentCrate),
2647+
execQuery(query, filterCrates, window.currentCrate),
26552648
params.go_to_first,
26562649
filterCrates);
26572650
}
@@ -2920,12 +2913,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29202913

29212914
function buildIndex(rawSearchIndex) {
29222915
searchIndex = [];
2923-
/**
2924-
* List of normalized search words (ASCII lowercased, and undescores removed).
2925-
*
2926-
* @type {Array<string>}
2927-
*/
2928-
const searchWords = [];
29292916
typeNameIdMap = new Map();
29302917
const charA = "A".charCodeAt(0);
29312918
let currentIndex = 0;
@@ -3004,7 +2991,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
30042991
* }}
30052992
*/
30062993
for (const [crate, crateCorpus] of rawSearchIndex) {
3007-
searchWords.push(crate);
30082994
// This object should have exactly the same set of fields as the "row"
30092995
// object defined below. Your JavaScript runtime will thank you.
30102996
// https://mathiasbynens.be/notes/shapes-ics
@@ -3017,6 +3003,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
30173003
parent: undefined,
30183004
type: null,
30193005
id: id,
3006+
word: crate,
30203007
normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
30213008
deprecated: null,
30223009
implDisambiguator: null,
@@ -3084,12 +3071,9 @@ ${item.displayPath}<span class="${type}">${name}</span>\
30843071
len = itemTypes.length;
30853072
for (let i = 0; i < len; ++i) {
30863073
let word = "";
3087-
// This object should have exactly the same set of fields as the "crateRow"
3088-
// object defined above.
30893074
if (typeof itemNames[i] === "string") {
30903075
word = itemNames[i].toLowerCase();
30913076
}
3092-
searchWords.push(word);
30933077
const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
30943078
let type = null;
30953079
if (itemFunctionSearchTypes[i] !== 0) {
@@ -3113,6 +3097,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
31133097
}
31143098
}
31153099
}
3100+
// This object should have exactly the same set of fields as the "crateRow"
3101+
// object defined above.
31163102
const row = {
31173103
crate: crate,
31183104
ty: itemTypes.charCodeAt(i) - charA,
@@ -3122,6 +3108,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
31223108
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
31233109
type,
31243110
id: id,
3111+
word,
31253112
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
31263113
deprecated: deprecatedItems.has(i),
31273114
implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
@@ -3153,7 +3140,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
31533140
}
31543141
currentIndex += itemTypes.length;
31553142
}
3156-
return searchWords;
31573143
}
31583144

31593145
/**
@@ -3332,10 +3318,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
33323318
search(true);
33333319
}
33343320

3335-
/**
3336-
* @type {Array<string>}
3337-
*/
3338-
const searchWords = buildIndex(rawSearchIndex);
3321+
buildIndex(rawSearchIndex);
33393322
if (typeof window !== "undefined") {
33403323
registerSearchEvents();
33413324
// If there's a search term in the URL, execute the search now.
@@ -3349,7 +3332,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
33493332
exports.execQuery = execQuery;
33503333
exports.parseQuery = parseQuery;
33513334
}
3352-
return searchWords;
33533335
}
33543336

33553337
if (typeof window !== "undefined") {

src/tools/rustdoc-js/tester.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,16 @@ function loadSearchJS(doc_folder, resource_suffix) {
396396
const staticFiles = path.join(doc_folder, "static.files");
397397
const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/));
398398
const searchModule = require(path.join(staticFiles, searchJs));
399-
const searchWords = searchModule.initSearch(searchIndex.searchIndex);
399+
searchModule.initSearch(searchIndex.searchIndex);
400400

401401
return {
402402
doSearch: function(queryStr, filterCrate, currentCrate) {
403-
return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
403+
return searchModule.execQuery(searchModule.parseQuery(queryStr),
404404
filterCrate, currentCrate);
405405
},
406406
getCorrections: function(queryStr, filterCrate, currentCrate) {
407407
const parsedQuery = searchModule.parseQuery(queryStr);
408-
searchModule.execQuery(parsedQuery, searchWords, filterCrate, currentCrate);
408+
searchModule.execQuery(parsedQuery, filterCrate, currentCrate);
409409
return parsedQuery.correction;
410410
},
411411
parseQuery: searchModule.parseQuery,

tests/rustdoc-js/substring.js

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
const EXPECTED = {
2-
'query': 'waker_from',
3-
'others': [
4-
{ 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' },
5-
{ 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' },
6-
],
7-
};
1+
const EXPECTED = [
2+
{
3+
'query': 'waker_from',
4+
'others': [
5+
{ 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' },
6+
{ 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' },
7+
],
8+
},
9+
{
10+
'query': 'my',
11+
'others': [
12+
{ 'path': 'substring', 'name': 'm_y_substringmatching' },
13+
],
14+
},
15+
];

tests/rustdoc-js/substring.rs

+2
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ impl SuperWakerTask {
1919
pub fn waker_non_local() {}
2020
pub fn from_non_local() {}
2121
}
22+
23+
pub fn m_y_substringmatching() {}

0 commit comments

Comments
 (0)