diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md
new file mode 100644
index 0000000000000..1fd859ad7cf49
--- /dev/null
+++ b/src/librustdoc/html/static/js/README.md
@@ -0,0 +1,15 @@
+# Rustdoc JS
+
+These JavaScript files are incorporated into the rustdoc binary at build time,
+and are minified and written to the filesystem as part of the doc build process.
+
+We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler)
+dialect of JSDoc to comment our code and annotate params and return types.
+To run a check:
+
+ ./x.py doc library/std
+ npm i -g google-closure-compiler
+ google-closure-compiler -W VERBOSE \
+ build//doc/{search-index*.js,crates*.js} \
+ src/librustdoc/html/static/js/{search.js,main.js,storage.js} \
+ --externs src/librustdoc/html/static/js/externs.js >/dev/null
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
new file mode 100644
index 0000000000000..629f90728d2f6
--- /dev/null
+++ b/src/librustdoc/html/static/js/externs.js
@@ -0,0 +1,32 @@
+// This file contains type definitions that are processed by the Closure Compiler but are
+// not put into the JavaScript we include as part of the documentation. It is used for
+// type checking. See README.md in this directory for more info.
+
+/* eslint-disable */
+var searchState;
+function initSearch(searchIndex){}
+
+/**
+ * @typedef {{
+ * raw: string,
+ * query: string,
+ * type: string,
+ * id: string,
+ * }}
+ */
+var ParsedQuery;
+
+/**
+ * @typedef {{
+ * crate: string,
+ * desc: string,
+ * id: number,
+ * name: string,
+ * normalizedName: string,
+ * parent: (Object|null|undefined),
+ * path: string,
+ * ty: (Number|null|number),
+ * type: (Array>|null)
+ * }}
+ */
+var Row;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 411a94ef2d1c1..f81f6d5d61fed 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -420,6 +420,13 @@ function hideThemeButtonState() {
return document.getElementById("help");
}
+ /**
+ * Show the help popup.
+ *
+ * @param {boolean} display - Whether to show or hide the popup
+ * @param {Event} ev - The event that triggered this call
+ * @param {Element} [help] - The help element if it already exists
+ */
function displayHelp(display, ev, help) {
if (display) {
help = help ? help : getHelpElement(true);
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 971bd3930cffd..cf320f7b4958a 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -113,7 +113,15 @@ window.initSearch = function(rawSearchIndex) {
var INPUTS_DATA = 0;
var OUTPUT_DATA = 1;
var NO_TYPE_FILTER = -1;
- var currentResults, index, searchIndex;
+ /**
+ * @type {Array}
+ */
+ var searchIndex;
+ /**
+ * @type {Array}
+ */
+ var searchWords;
+ var currentResults;
var ALIASES = {};
var params = searchState.getQueryStringParams();
@@ -126,12 +134,15 @@ window.initSearch = function(rawSearchIndex) {
}
/**
- * Executes the query and builds an index of results
- * @param {[Object]} query [The user query]
- * @param {[type]} searchWords [The list of search words to query
- * against]
- * @param {[type]} filterCrates [Crate to search in if defined]
- * @return {[type]} [A search index of results]
+ * Executes the query and returns a list of results for each results tab.
+ * @param {Object} query - The user query
+ * @param {Array} searchWords - The list of search words to query against
+ * @param {string} [filterCrates] - Crate to search in
+ * @return {{
+ * in_args: Array>,
+ * returned: Array>,
+ * others: Array>
+ * }}
*/
function execQuery(query, searchWords, filterCrates) {
function itemTypeFromName(typename) {
@@ -847,11 +858,11 @@ window.initSearch = function(rawSearchIndex) {
* This could be written functionally, but I wanted to minimise
* functions on stack.
*
- * @param {[string]} name [The name of the result]
- * @param {[string]} path [The path of the result]
- * @param {[string]} keys [The keys to be used (["file", "open"])]
- * @param {[object]} parent [The parent of the result]
- * @return {boolean} [Whether the result is valid or not]
+ * @param {string} name - The name of the result
+ * @param {string} path - The path of the result
+ * @param {string} keys - The keys to be used (["file", "open"])
+ * @param {Object} parent - The parent of the result
+ * @return {boolean} - Whether the result is valid or not
*/
function validateResult(name, path, keys, parent) {
for (var i = 0, len = keys.length; i < len; ++i) {
@@ -872,8 +883,14 @@ window.initSearch = function(rawSearchIndex) {
return true;
}
+ /**
+ * Parse a string into a query object.
+ *
+ * @param {string} raw - The text that the user typed.
+ * @returns {ParsedQuery}
+ */
function getQuery(raw) {
- var matches, type, query;
+ var matches, type = "", query;
query = raw;
matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
@@ -974,6 +991,12 @@ window.initSearch = function(rawSearchIndex) {
return tmp;
}
+ /**
+ * Render a set of search results for a single tab.
+ * @param {Array>} array - The search results for this tab
+ * @param {ParsedQuery} query
+ * @param {boolean} display - True if this is the active tab
+ */
function addTab(array, query, display) {
var extraClass = "";
if (display === true) {
@@ -1083,7 +1106,7 @@ window.initSearch = function(rawSearchIndex) {
currentResults = query.id;
- var ret_others = addTab(results.others, query);
+ var ret_others = addTab(results.others, query, true);
var ret_in_args = addTab(results.in_args, query, false);
var ret_returned = addTab(results.returned, query, false);
@@ -1253,6 +1276,12 @@ window.initSearch = function(rawSearchIndex) {
return undefined;
}
+ /**
+ * Perform a search based on the current state of the search input element
+ * and display the results.
+ * @param {Event} [e] - The event that triggered this search, if any
+ * @param {boolean} [forced]
+ */
function search(e, forced) {
var params = searchState.getQueryStringParams();
var query = getQuery(searchState.input.value.trim());
@@ -1287,11 +1316,14 @@ window.initSearch = function(rawSearchIndex) {
}
var filterCrates = getFilterCrates();
- showResults(execSearch(query, index, filterCrates), params.go_to_first);
+ showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"]);
}
function buildIndex(rawSearchIndex) {
searchIndex = [];
+ /**
+ * @type {Array}
+ */
var searchWords = [];
var i, word;
var currentIndex = 0;
@@ -1304,6 +1336,38 @@ window.initSearch = function(rawSearchIndex) {
var crateSize = 0;
+ /**
+ * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
+ * are arrays with the same length. n[i] contains the name of an item.
+ * t[i] contains the type of that item (as a small integer that represents an
+ * offset in `itemTypes`). d[i] contains the description of that item.
+ *
+ * q[i] contains the full path of the item, or an empty string indicating
+ * "same as q[i-1]".
+ *
+ * i[i], f[i] are a mystery.
+ *
+ * `a` defines aliases with an Array of pairs: [name, offset], where `offset`
+ * points into the n/t/d/q/i/f arrays.
+ *
+ * `doc` contains the description of the crate.
+ *
+ * `p` is a mystery and isn't the same length as n/t/d/q/i/f.
+ *
+ * @type {{
+ * doc: string,
+ * a: Object,
+ * n: Array,
+ * t: Array,
+ * d: Array,
+ * q: Array,
+ * i: Array,
+ * f: Array>,
+ * p: Array