Skip to content

Commit 9f68b6e

Browse files
authored
Rollup merge of #92026 - jsha:jsdoc-search, r=GuillaumeGomez
Add some JSDoc comments to rustdoc JS This follows the Closure Compiler dialect of JSDoc, so we can use it to do some basic type checking. We don't plan to compile with Closure Compiler, just use it to check types. See https://github.com/google/closure-compiler/wiki/ for details. To try checking the annotations, run: ``` npm i -g google-closure-compiler google-closure-compiler -W VERBOSE build/x86_64-unknown-linux-gnu/doc/{search-index1.59.0.js,crates1.59.0.js} src/librustdoc/html/static/js/{search.js,main.js,storage.js} --externs src/librustdoc/html/static/js/externs.js >/dev/null ``` You'll see some warnings that "String continuations are not recommended". I'm not addressing those right now. [Discussed on Zulip](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/doc.20format.20for.20JS/near/265209466). r? `@GuillaumeGomez`
2 parents d8bf974 + 7ba086c commit 9f68b6e

File tree

5 files changed

+170
-30
lines changed

5 files changed

+170
-30
lines changed
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Rustdoc JS
2+
3+
These JavaScript files are incorporated into the rustdoc binary at build time,
4+
and are minified and written to the filesystem as part of the doc build process.
5+
6+
We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler)
7+
dialect of JSDoc to comment our code and annotate params and return types.
8+
To run a check:
9+
10+
./x.py doc library/std
11+
npm i -g google-closure-compiler
12+
google-closure-compiler -W VERBOSE \
13+
build/<YOUR PLATFORM>/doc/{search-index*.js,crates*.js} \
14+
src/librustdoc/html/static/js/{search.js,main.js,storage.js} \
15+
--externs src/librustdoc/html/static/js/externs.js >/dev/null
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This file contains type definitions that are processed by the Closure Compiler but are
2+
// not put into the JavaScript we include as part of the documentation. It is used for
3+
// type checking. See README.md in this directory for more info.
4+
5+
/* eslint-disable */
6+
var searchState;
7+
function initSearch(searchIndex){}
8+
9+
/**
10+
* @typedef {{
11+
* raw: string,
12+
* query: string,
13+
* type: string,
14+
* id: string,
15+
* }}
16+
*/
17+
var ParsedQuery;
18+
19+
/**
20+
* @typedef {{
21+
* crate: string,
22+
* desc: string,
23+
* id: number,
24+
* name: string,
25+
* normalizedName: string,
26+
* parent: (Object|null|undefined),
27+
* path: string,
28+
* ty: (Number|null|number),
29+
* type: (Array<?>|null)
30+
* }}
31+
*/
32+
var Row;

src/librustdoc/html/static/js/main.js

+7
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,13 @@ function hideThemeButtonState() {
420420
return document.getElementById("help");
421421
}
422422

423+
/**
424+
* Show the help popup.
425+
*
426+
* @param {boolean} display - Whether to show or hide the popup
427+
* @param {Event} ev - The event that triggered this call
428+
* @param {Element} [help] - The help element if it already exists
429+
*/
423430
function displayHelp(display, ev, help) {
424431
if (display) {
425432
help = help ? help : getHelpElement(true);

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

+100-30
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,15 @@ window.initSearch = function(rawSearchIndex) {
113113
var INPUTS_DATA = 0;
114114
var OUTPUT_DATA = 1;
115115
var NO_TYPE_FILTER = -1;
116-
var currentResults, index, searchIndex;
116+
/**
117+
* @type {Array<Row>}
118+
*/
119+
var searchIndex;
120+
/**
121+
* @type {Array<string>}
122+
*/
123+
var searchWords;
124+
var currentResults;
117125
var ALIASES = {};
118126
var params = searchState.getQueryStringParams();
119127

@@ -126,12 +134,15 @@ window.initSearch = function(rawSearchIndex) {
126134
}
127135

128136
/**
129-
* Executes the query and builds an index of results
130-
* @param {[Object]} query [The user query]
131-
* @param {[type]} searchWords [The list of search words to query
132-
* against]
133-
* @param {[type]} filterCrates [Crate to search in if defined]
134-
* @return {[type]} [A search index of results]
137+
* Executes the query and returns a list of results for each results tab.
138+
* @param {Object} query - The user query
139+
* @param {Array<string>} searchWords - The list of search words to query against
140+
* @param {string} [filterCrates] - Crate to search in
141+
* @return {{
142+
* in_args: Array<?>,
143+
* returned: Array<?>,
144+
* others: Array<?>
145+
* }}
135146
*/
136147
function execQuery(query, searchWords, filterCrates) {
137148
function itemTypeFromName(typename) {
@@ -847,11 +858,11 @@ window.initSearch = function(rawSearchIndex) {
847858
* This could be written functionally, but I wanted to minimise
848859
* functions on stack.
849860
*
850-
* @param {[string]} name [The name of the result]
851-
* @param {[string]} path [The path of the result]
852-
* @param {[string]} keys [The keys to be used (["file", "open"])]
853-
* @param {[object]} parent [The parent of the result]
854-
* @return {boolean} [Whether the result is valid or not]
861+
* @param {string} name - The name of the result
862+
* @param {string} path - The path of the result
863+
* @param {string} keys - The keys to be used (["file", "open"])
864+
* @param {Object} parent - The parent of the result
865+
* @return {boolean} - Whether the result is valid or not
855866
*/
856867
function validateResult(name, path, keys, parent) {
857868
for (var i = 0, len = keys.length; i < len; ++i) {
@@ -872,8 +883,14 @@ window.initSearch = function(rawSearchIndex) {
872883
return true;
873884
}
874885

886+
/**
887+
* Parse a string into a query object.
888+
*
889+
* @param {string} raw - The text that the user typed.
890+
* @returns {ParsedQuery}
891+
*/
875892
function getQuery(raw) {
876-
var matches, type, query;
893+
var matches, type = "", query;
877894
query = raw;
878895

879896
matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
@@ -974,6 +991,12 @@ window.initSearch = function(rawSearchIndex) {
974991
return tmp;
975992
}
976993

994+
/**
995+
* Render a set of search results for a single tab.
996+
* @param {Array<?>} array - The search results for this tab
997+
* @param {ParsedQuery} query
998+
* @param {boolean} display - True if this is the active tab
999+
*/
9771000
function addTab(array, query, display) {
9781001
var extraClass = "";
9791002
if (display === true) {
@@ -1083,7 +1106,7 @@ window.initSearch = function(rawSearchIndex) {
10831106

10841107
currentResults = query.id;
10851108

1086-
var ret_others = addTab(results.others, query);
1109+
var ret_others = addTab(results.others, query, true);
10871110
var ret_in_args = addTab(results.in_args, query, false);
10881111
var ret_returned = addTab(results.returned, query, false);
10891112

@@ -1253,6 +1276,12 @@ window.initSearch = function(rawSearchIndex) {
12531276
return undefined;
12541277
}
12551278

1279+
/**
1280+
* Perform a search based on the current state of the search input element
1281+
* and display the results.
1282+
* @param {Event} [e] - The event that triggered this search, if any
1283+
* @param {boolean} [forced]
1284+
*/
12561285
function search(e, forced) {
12571286
var params = searchState.getQueryStringParams();
12581287
var query = getQuery(searchState.input.value.trim());
@@ -1287,11 +1316,14 @@ window.initSearch = function(rawSearchIndex) {
12871316
}
12881317

12891318
var filterCrates = getFilterCrates();
1290-
showResults(execSearch(query, index, filterCrates), params.go_to_first);
1319+
showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"]);
12911320
}
12921321

12931322
function buildIndex(rawSearchIndex) {
12941323
searchIndex = [];
1324+
/**
1325+
* @type {Array<string>}
1326+
*/
12951327
var searchWords = [];
12961328
var i, word;
12971329
var currentIndex = 0;
@@ -1304,6 +1336,38 @@ window.initSearch = function(rawSearchIndex) {
13041336

13051337
var crateSize = 0;
13061338

1339+
/**
1340+
* The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
1341+
* are arrays with the same length. n[i] contains the name of an item.
1342+
* t[i] contains the type of that item (as a small integer that represents an
1343+
* offset in `itemTypes`). d[i] contains the description of that item.
1344+
*
1345+
* q[i] contains the full path of the item, or an empty string indicating
1346+
* "same as q[i-1]".
1347+
*
1348+
* i[i], f[i] are a mystery.
1349+
*
1350+
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
1351+
* points into the n/t/d/q/i/f arrays.
1352+
*
1353+
* `doc` contains the description of the crate.
1354+
*
1355+
* `p` is a mystery and isn't the same length as n/t/d/q/i/f.
1356+
*
1357+
* @type {{
1358+
* doc: string,
1359+
* a: Object,
1360+
* n: Array<string>,
1361+
* t: Array<Number>,
1362+
* d: Array<string>,
1363+
* q: Array<string>,
1364+
* i: Array<Number>,
1365+
* f: Array<Array<?>>,
1366+
* p: Array<Object>,
1367+
* }}
1368+
*/
1369+
var crateCorpus = rawSearchIndex[crate];
1370+
13071371
searchWords.push(crate);
13081372
// This object should have exactly the same set of fields as the "row"
13091373
// object defined below. Your JavaScript runtime will thank you.
@@ -1313,7 +1377,7 @@ window.initSearch = function(rawSearchIndex) {
13131377
ty: 1, // == ExternCrate
13141378
name: crate,
13151379
path: "",
1316-
desc: rawSearchIndex[crate].doc,
1380+
desc: crateCorpus.doc,
13171381
parent: undefined,
13181382
type: null,
13191383
id: id,
@@ -1324,23 +1388,23 @@ window.initSearch = function(rawSearchIndex) {
13241388
currentIndex += 1;
13251389

13261390
// an array of (Number) item types
1327-
var itemTypes = rawSearchIndex[crate].t;
1391+
var itemTypes = crateCorpus.t;
13281392
// an array of (String) item names
1329-
var itemNames = rawSearchIndex[crate].n;
1393+
var itemNames = crateCorpus.n;
13301394
// an array of (String) full paths (or empty string for previous path)
1331-
var itemPaths = rawSearchIndex[crate].q;
1395+
var itemPaths = crateCorpus.q;
13321396
// an array of (String) descriptions
1333-
var itemDescs = rawSearchIndex[crate].d;
1397+
var itemDescs = crateCorpus.d;
13341398
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
1335-
var itemParentIdxs = rawSearchIndex[crate].i;
1399+
var itemParentIdxs = crateCorpus.i;
13361400
// an array of (Object | null) the type of the function, if any
1337-
var itemFunctionSearchTypes = rawSearchIndex[crate].f;
1401+
var itemFunctionSearchTypes = crateCorpus.f;
13381402
// an array of [(Number) item type,
13391403
// (String) name]
1340-
var paths = rawSearchIndex[crate].p;
1404+
var paths = crateCorpus.p;
13411405
// an array of [(String) alias name
13421406
// [Number] index to items]
1343-
var aliases = rawSearchIndex[crate].a;
1407+
var aliases = crateCorpus.a;
13441408

13451409
// convert `rawPaths` entries into object form
13461410
var len = paths.length;
@@ -1406,6 +1470,16 @@ window.initSearch = function(rawSearchIndex) {
14061470
return searchWords;
14071471
}
14081472

1473+
/**
1474+
* Callback for when the search form is submitted.
1475+
* @param {Event} [e] - The event that triggered this call, if any
1476+
*/
1477+
function onSearchSubmit(e) {
1478+
e.preventDefault();
1479+
searchState.clearInputTimeout();
1480+
search();
1481+
}
1482+
14091483
function registerSearchEvents() {
14101484
var searchAfter500ms = function() {
14111485
searchState.clearInputTimeout();
@@ -1421,11 +1495,7 @@ window.initSearch = function(rawSearchIndex) {
14211495
};
14221496
searchState.input.onkeyup = searchAfter500ms;
14231497
searchState.input.oninput = searchAfter500ms;
1424-
document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
1425-
e.preventDefault();
1426-
searchState.clearInputTimeout();
1427-
search();
1428-
};
1498+
document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit;
14291499
searchState.input.onchange = function(e) {
14301500
if (e.target !== document.activeElement) {
14311501
// To prevent doing anything when it's from a blur event.
@@ -1546,7 +1616,7 @@ window.initSearch = function(rawSearchIndex) {
15461616
};
15471617
}
15481618

1549-
index = buildIndex(rawSearchIndex);
1619+
searchWords = buildIndex(rawSearchIndex);
15501620
registerSearchEvents();
15511621
// If there's a search term in the URL, execute the search now.
15521622
if (searchState.getQueryStringParams().search) {

src/librustdoc/html/static/js/storage.js

+16
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ function removeClass(elem, className) {
5555
elem.classList.remove(className);
5656
}
5757

58+
/**
59+
* Run a callback for every element of an Array.
60+
* @param {Array<?>} arr - The array to iterate over
61+
* @param {function(?)} func - The callback
62+
* @param {boolean} [reversed] - Whether to iterate in reverse
63+
*/
5864
function onEach(arr, func, reversed) {
5965
if (arr && arr.length > 0 && func) {
6066
var length = arr.length;
@@ -76,6 +82,16 @@ function onEach(arr, func, reversed) {
7682
return false;
7783
}
7884

85+
/**
86+
* Turn an HTMLCollection or a NodeList into an Array, then run a callback
87+
* for every element. This is useful because iterating over an HTMLCollection
88+
* or a "live" NodeList while modifying it can be very slow.
89+
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection
90+
* https://developer.mozilla.org/en-US/docs/Web/API/NodeList
91+
* @param {NodeList<?>|HTMLCollection<?>} lazyArray - An array to iterate over
92+
* @param {function(?)} func - The callback
93+
* @param {boolean} [reversed] - Whether to iterate in reverse
94+
*/
7995
function onEachLazy(lazyArray, func, reversed) {
8096
return onEach(
8197
Array.prototype.slice.call(lazyArray),

0 commit comments

Comments
 (0)