diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index defdc20132e..141d76d59e1 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -81,3 +81,56 @@ let ResultsTable; * }} */ let Results; + +/** + * A pair of [inputs, outputs], or 0 for null. This is gets stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * An input or output can be encoded as just a number if there is only one of them, AND + * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had + * a function with a single output, and that output had a single generic: + * + * fn something() -> Result + * + * If output was allowed to be any RawFunctionType, it would look like this + * + * [[], [50, [3, 3]]] + * + * The problem is that the above output could be interpreted as either a type with ID 50 and two + * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second + * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing + * in favor of the pair of types interpretation. This is why the `(number|Array)` + * is used instead of `(RawFunctionType|Array)`. + * + * @typedef {( + * 0 | + * [(number|Array)] | + * [(number|Array), (number|Array)] + * )} + */ +let RawFunctionSearchType; + +/** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * @typedef {number | [number, Array]} + */ +let RawFunctionType; + +/** + * @typedef {{ + * inputs: Array, + * outputs: Array, + * }} + */ +let FunctionSearchType; + +/** + * @typedef {{ + * name: (null|string), + * ty: (null|number), + * generics: Array, + * }} + */ +let FunctionType; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 54057627c92..a766dd68e10 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1825,6 +1825,24 @@ function initSearch(rawSearchIndex) { filterCrates); } + /** + * Convert a list of RawFunctionType / ID to object-based FunctionType. + * + * Crates often have lots of functions in them, and it's common to have a large number of + * functions that operate on a small set of data types, so the search index compresses them + * by encoding function parameter and return types as indexes into an array of names. + * + * Even when a general-purpose compression algorithm is used, this is still a win. I checked. + * https://github.com/rust-lang/rust/pull/98475#issue-1284395985 + * + * The format for individual function types is encoded in + * librustdoc/html/render/mod.rs: impl Serialize for RenderType + * + * @param {null|Array} types + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {Array} + */ function buildItemSearchTypeAll(types, lowercasePaths) { const PATH_INDEX_DATA = 0; const GENERICS_DATA = 1; @@ -1848,6 +1866,21 @@ function initSearch(rawSearchIndex) { }); } + /** + * Convert from RawFunctionSearchType to FunctionSearchType. + * + * Crates often have lots of functions in them, and function signatures are sometimes complex, + * so rustdoc uses a pretty tight encoding for them. This function converts it to a simpler, + * object-based encoding so that the actual search code is more readable and easier to debug. + * + * The raw function search type format is generated using serde in + * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType + * + * @param {RawFunctionSearchType} functionSearchType + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {null|FunctionSearchType} + */ function buildFunctionSearchType(functionSearchType, lowercasePaths) { const INPUTS_DATA = 0; const OUTPUT_DATA = 1; @@ -1935,7 +1968,7 @@ function initSearch(rawSearchIndex) { * d: Array, * q: Array, * i: Array, - * f: Array<0 | Object>, + * f: Array, * p: Array, * }} */