diff options
Diffstat (limited to 'src/librustdoc/html/static')
| -rw-r--r-- | src/librustdoc/html/static/css/rustdoc.css | 76 | ||||
| -rw-r--r-- | src/librustdoc/html/static/css/themes/ayu.css | 67 | ||||
| -rw-r--r-- | src/librustdoc/html/static/css/themes/dark.css | 80 | ||||
| -rw-r--r-- | src/librustdoc/html/static/css/themes/light.css | 82 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/externs.js | 59 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/main.js | 42 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/search.js | 163 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/source-script.js | 15 |
8 files changed, 294 insertions, 290 deletions
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 5d0756d30fb..f7b4fdb736c 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -114,6 +114,9 @@ body { -webkit-font-feature-settings: "kern", "liga"; -moz-font-feature-settings: "kern", "liga"; font-feature-settings: "kern", "liga"; + + background-color: var(--main-background-color); + color: var(--main-color); } h1 { @@ -158,8 +161,8 @@ h1.fqn { Underlines elsewhere in the documentation break up visual flow and tend to invert section hierarchies. */ h2, -.top-doc h3, -.top-doc h4 { +.top-doc .docblock > h3, +.top-doc .docblock > h4 { border-bottom: 1px solid; } h3.code-header { @@ -214,6 +217,26 @@ a.srclink, font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; } +h1, h2, h3, h4, +a#toggle-all-docs, +a.anchor, +.small-section-header a, +#source-sidebar a, +pre.rust a, +.sidebar h2 a, +.sidebar h3 a, +.mobile-topbar h2 a, +.in-band a, +.search-results a, +.module-item .stab, +.import-item .stab, +.result-name .primitive > i, .result-name .keyword > i, +.content .method .where, +.content .fn .where, +.content .where.fmt-newline { + color: var(--main-color); +} + ol, ul { padding-left: 24px; } @@ -391,6 +414,14 @@ nav.sub { display: none; } +.source .sidebar, #sidebar-toggle, #source-sidebar { + background-color: var(--sidebar-background-color); +} + +#sidebar-toggle:hover { + background-color: var(--sidebar-background-color-hover); +} + .source .sidebar > *:not(#sidebar-toggle) { opacity: 0; visibility: hidden; @@ -629,11 +660,6 @@ h2.location a { display: block; } -.invisible { - width: 100%; - display: inline-block; -} - .content .in-band { flex-grow: 1; margin: 0px; @@ -1008,6 +1034,11 @@ table, top: -5px; } +.popover, .popover::before { + background-color: var(--main-background-color); + color: var(--main-color); +} + #help-button .popover { max-width: 600px; } @@ -1428,6 +1459,25 @@ pre.rust { animation: rotating 2s linear infinite; } +.setting-line .radio-line input:checked { + box-shadow: inset 0 0 0 3px var(--main-background-color); + background-color: var(--settings-input-color); +} +.setting-line .radio-line input:focus { + box-shadow: 0 0 1px 1px var(--settings-input-color); +} +/* In here we combine both `:focus` and `:checked` properties. */ +.setting-line .radio-line input:checked:focus { + box-shadow: inset 0 0 0 3px var(--main-background-color), + 0 0 2px 2px var(--settings-input-color); +} +.setting-line .radio-line input:hover { + border-color: var(--settings-input-color) !important; +} +input:checked + .slider { + background-color: var(--settings-input-color); +} + #help-button > button { font-family: "Fira Sans", Arial, sans-serif; text-align: center; @@ -1681,6 +1731,11 @@ details.rustdoc-toggle[open] > summary.hideme::after { content: "Collapse"; } +/* This is needed in docblocks to have the "▶" element to be on the same line. */ +.docblock summary > * { + display: inline-block; +} + /* Media Queries */ @media (min-width: 701px) { @@ -1772,9 +1827,11 @@ details.rustdoc-toggle[open] > summary.hideme::after { /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, so don't bump down the main content or the sidebar. */ .source main, - .source .sidebar { + .rustdoc.source .sidebar { top: 0; padding: 0; + height: 100vh; + border: 0; } .sidebar.shown, @@ -1924,6 +1981,9 @@ details.rustdoc-toggle[open] > summary.hideme::after { width: unset; border-top-right-radius: unset; border-bottom-right-radius: unset; + position: sticky; + border: 0; + border-bottom: 1px solid; } #source-sidebar { diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index b7d0db1f002..7756e877ef7 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -3,30 +3,12 @@ Based off of the Ayu theme Original by Dempfi (https://github.com/dempfi/ayu) */ -/* General structure and fonts */ - -body, .popover, .popover::before { - background-color: #0f1419; - color: #c5c5c5; -} - -.setting-line .radio-line input { - border-color: #c5c5c5; -} -.setting-line .radio-line input:checked { - box-shadow: inset 0 0 0 3px #0f1419; - background-color: #ffb454; -} -.setting-line .radio-line input:focus { - box-shadow: 0 0 1px 1px #ffb454; -} -/* In here we combine both `:focus` and `:checked` properties. */ -.setting-line .radio-line input:checked:focus { - box-shadow: inset 0 0 0 3px 0f1419, - 0 0 2px 2px #ffb454; -} -.setting-line .radio-line input:hover { - border-color: #ffb454 !important; +:root { + --main-background-color: #0f1419; + --main-color: #c5c5c5; + --settings-input-color: #ffb454; + --sidebar-background-color: #14191f; + --sidebar-background-color-hover: rgba(70, 70, 70, 0.33); } .slider { @@ -35,9 +17,6 @@ body, .popover, .popover::before { .slider:before { background-color: white; } -input:checked + .slider { - background-color: #ffb454; -} input:focus + .slider { box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); } @@ -62,10 +41,6 @@ h4 { background-color: #0f1419; } -.invisible { - background: rgba(0, 0, 0, 0); -} - .docblock code { color: #ffb454; } @@ -129,10 +104,6 @@ pre, .rustdoc.source .example-wrap { color: #ffb44c; } -.source .sidebar { - background-color: #14191f; -} - .sidebar-elems .location { color: #ff7733; } @@ -153,12 +124,6 @@ pre, .rustdoc.source .example-wrap { border-color: #5c6773; } -.content .method .where, -.content .fn .where, -.content .where.fmt-newline { - color: #c5c5c5; -} - .search-results a:hover { background-color: #777; } @@ -233,17 +198,6 @@ a { color: #39AFD7; } -a#toggle-all-docs, -a.anchor, -.small-section-header a, -#source-sidebar a, -pre.rust a, -.sidebar h2 a, -.sidebar h3 a, -.mobile-topbar h2 a, -.in-band a { - color: #c5c5c5; -} .sidebar h2 a, .sidebar h3 a { color: white; @@ -617,15 +571,6 @@ kbd { color: #999; } -#sidebar-toggle { - background-color: #14191f; -} -#sidebar-toggle:hover { - background-color: rgba(70, 70, 70, 0.33); -} -#source-sidebar { - background-color: #14191f; -} #source-sidebar > .title { color: #fff; border-bottom-color: #5c6773; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index eb64ef3e771..04d5778f59c 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -1,25 +1,9 @@ -body, .popover, .popover::before { - background-color: #353535; - color: #ddd; -} - -.setting-line .radio-line input { - border-color: #ddd; -} -.setting-line .radio-line input:checked { - box-shadow: inset 0 0 0 3px #353535; - background-color: #2196f3; -} -.setting-line .radio-line input:focus { - box-shadow: 0 0 1px 1px #2196f3; -} -/* In here we combine both `:focus` and `:checked` properties. */ -.setting-line .radio-line input:checked:focus { - box-shadow: inset 0 0 0 3px #353535, - 0 0 2px 2px #2196f3; -} -.setting-line .radio-line input:hover { - border-color: #2196f3 !important; +:root { + --main-background-color: #353535; + --main-color: #ddd; + --settings-input-color: #2196f3; + --sidebar-background-color: #565656; + --sidebar-background-color-hover: #676767; } .slider { @@ -28,16 +12,10 @@ body, .popover, .popover::before { .slider:before { background-color: white; } -input:checked + .slider { - background-color: #2196F3; -} input:focus + .slider { box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); } -h1, h2, h3, h4 { - color: #ddd; -} h1.fqn { border-bottom-color: #d2d2d2; } @@ -49,10 +27,6 @@ h2, h3, h4 { background-color: #353535; } -.invisible { - background: rgba(0, 0, 0, 0); -} - .docblock code, .docblock-short code { background-color: #2A2A2A; } @@ -98,10 +72,6 @@ pre, .rustdoc.source .example-wrap { background: #444; } -.source .sidebar { - background-color: #565656; -} - .line-numbers span { color: #3B91E2; } .line-numbers .line-highlighted { background-color: #0a042f !important; @@ -115,12 +85,6 @@ pre, .rustdoc.source .example-wrap { border-color: #ddd; } -.content .method .where, -.content .fn .where, -.content .where.fmt-newline { - color: #ddd; -} - .search-results a:hover { background-color: #777; } @@ -214,20 +178,6 @@ a { color: #D2991D; } -a#toggle-all-docs, -a.anchor, -.small-section-header a, -#source-sidebar a, -pre.rust a, -.sidebar h2 a, -.sidebar h3 a, -.mobile-topbar h2 a, -.in-band a { - color: #ddd; -} -.search-results a { - color: #ddd; -} a.test-arrow { color: #dedede; } @@ -261,11 +211,6 @@ details.undocumented > summary::before { border-color: #008dfd; } -.module-item .stab, -.import-item .stab { - color: #ddd; -} - .stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; } .stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; } .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; } @@ -291,10 +236,6 @@ details.undocumented > summary::before { color: grey; } -.result-name .primitive > i, .result-name .keyword > i { - color: #ddd; -} - .line-numbers :target { background-color: transparent; } /* Code highlighting */ @@ -488,15 +429,6 @@ kbd { color: #ccc; } -#sidebar-toggle { - background-color: #565656; -} -#sidebar-toggle:hover { - background-color: #676767; -} -#source-sidebar { - background-color: #565656; -} #source-sidebar > .title { border-bottom-color: #ccc; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 00cdf835897..5310736037a 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -1,27 +1,9 @@ -/* General structure and fonts */ - -body, .popover, .popover::before { - background-color: white; - color: black; -} - -.setting-line .radio-line input { - border-color: black; -} -.setting-line .radio-line input:checked { - box-shadow: inset 0 0 0 3px white; - background-color: #2196f3; -} -.setting-line .radio-line input:focus { - box-shadow: 0 0 1px 1px #2196f3; -} -/* In here we combine both `:focus` and `:checked` properties. */ -.setting-line .radio-line input:checked:focus { - box-shadow: inset 0 0 0 3px white, - 0 0 2px 2px #2196f3; -} -.setting-line .radio-line input:hover { - border-color: #2196f3 !important; +:root { + --main-background-color: white; + --main-color: black; + --settings-input-color: #2196f3; + --sidebar-background-color: #F5F5F5; + --sidebar-background-color-hover: #E0E0E0; } .slider { @@ -30,16 +12,10 @@ body, .popover, .popover::before { .slider:before { background-color: white; } -input:checked + .slider { - background-color: #2196F3; -} input:focus + .slider { box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); } -h1, h2, h3, h4 { - color: black; -} h1.fqn { border-bottom-color: #DDDDDD; } @@ -51,10 +27,6 @@ h2, h3, h4 { background-color: white; } -.invisible { - background: rgba(0, 0, 0, 0); -} - .docblock code, .docblock-short code { background-color: #F5F5F5; } @@ -100,10 +72,6 @@ pre, .rustdoc.source .example-wrap { background-color: #fff; } -.source .sidebar { - background-color: #f1f1f1; -} - .line-numbers span { color: #c67e2d; } .line-numbers .line-highlighted { background-color: #FDFFD3 !important; @@ -117,12 +85,6 @@ pre, .rustdoc.source .example-wrap { border-color: #ddd; } -.content .method .where, -.content .fn .where, -.content .where.fmt-newline { - color: #4E4C4C; -} - .search-results a:hover { background-color: #ddd; } @@ -213,20 +175,6 @@ a { color: #3873AD; } -a#toggle-all-docs, -a.anchor, -.small-section-header a, -#source-sidebar a, -pre.rust a, -.sidebar h2 a, -.sidebar h3 a, -.mobile-topbar h2 a, -.in-band a { - color: #000; -} -.search-results a { - color: initial; -} a.test-arrow { color: #f5f5f5; } @@ -250,11 +198,6 @@ details.undocumented > summary::before { border-color: #66afe9; } -.module-item .stab, -.import-item .stab { - color: #000; -} - .stab.empty-impl { background: #FFF5D6; border-color: #FFC600; } .stab.unstable { background: #FFF5D6; border-color: #FFC600; } .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; } @@ -275,10 +218,6 @@ details.undocumented > summary::before { color: grey; } -.result-name .primitive > i, .result-name .keyword > i { - color: black; -} - .line-numbers :target { background-color: transparent; } /* Code highlighting */ @@ -472,15 +411,6 @@ kbd { color: #999; } -#sidebar-toggle { - background-color: #F5F5F5; -} -#sidebar-toggle:hover { - background-color: #E0E0E0; -} -#source-sidebar { - background-color: #F5F5F5; -} #source-sidebar > .title { border-bottom-color: #ccc; } diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index defdc20132e..ecbe15a59da 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -81,3 +81,62 @@ let ResultsTable; * }} */ let Results; + +/** + * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * 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<usize, usize> + * + * 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<RawFunctionType>)` + * is used instead of `(RawFunctionType|Array<RawFunctionType>)`. + * + * @typedef {( + * 0 | + * [(number|Array<RawFunctionType>)] | + * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] + * )} + */ +let RawFunctionSearchType; + +/** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * @typedef {number | [number, Array<RawFunctionType>]} + */ +let RawFunctionType; + +/** + * @typedef {{ + * inputs: Array<FunctionType>, + * outputs: Array<FunctionType>, + * }} + */ +let FunctionSearchType; + +/** + * @typedef {{ + * name: (null|string), + * ty: (null|number), + * generics: Array<FunctionType>, + * }} + */ +let FunctionType; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 70dbfd44425..6658f07ce01 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -4,40 +4,6 @@ "use strict"; -if (!String.prototype.startsWith) { - String.prototype.startsWith = function(searchString, position) { - position = position || 0; - return this.indexOf(searchString, position) === position; - }; -} -if (!String.prototype.endsWith) { - String.prototype.endsWith = function(suffix, length) { - const l = length || this.length; - return this.indexOf(suffix, l - suffix.length) !== -1; - }; -} - -if (!DOMTokenList.prototype.add) { - DOMTokenList.prototype.add = function(className) { - if (className && !hasClass(this, className)) { - if (this.className && this.className.length > 0) { - this.className += " " + className; - } else { - this.className = className; - } - } - }; -} - -if (!DOMTokenList.prototype.remove) { - DOMTokenList.prototype.remove = function(className) { - if (className && this.className) { - this.className = (" " + this.className + " ").replace(" " + className + " ", " ") - .trim(); - } - }; -} - // Get a value from the rustdoc-vars div, which is used to convey data from // Rust to the JS. If there is no such element, return null. function getVar(name) { @@ -412,14 +378,15 @@ function loadCss(cssFileName) { window.hidePopoverMenus(); } - const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; function handleShortcut(ev) { // Don't interfere with browser shortcuts + const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { return; } - if (document.activeElement.tagName === "INPUT") { + if (document.activeElement.tagName === "INPUT" && + document.activeElement.type !== "checkbox") { switch (getVirtualKey(ev)) { case "Escape": handleEscape(ev); @@ -926,6 +893,7 @@ function loadCss(cssFileName) { function showHelp() { const menu = getHelpMenu(true); if (menu.style.display === "none") { + window.hidePopoverMenus(); menu.style.display = ""; } } @@ -939,6 +907,8 @@ function loadCss(cssFileName) { const shouldShowHelp = menu.style.display === "none"; if (shouldShowHelp) { showHelp(); + } else { + window.hidePopoverMenus(); } }); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index cb1609d4983..75c7bd45a29 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -114,10 +114,6 @@ function levenshtein(s1, s2) { function initSearch(rawSearchIndex) { const MAX_LEV_DISTANCE = 3; const MAX_RESULTS = 200; - const GENERICS_DATA = 2; - const NAME = 0; - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; const NO_TYPE_FILTER = -1; /** * @type {Array<Row>} @@ -895,21 +891,18 @@ function initSearch(rawSearchIndex) { * @return {integer} - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`. */ function checkGenerics(row, elem, defaultLev) { - if (row.length <= GENERICS_DATA || row[GENERICS_DATA].length === 0) { - return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1; - } else if (row[GENERICS_DATA].length > 0 && row[GENERICS_DATA][0][NAME] === "") { - if (row.length > GENERICS_DATA) { - return checkGenerics(row[GENERICS_DATA][0], elem, defaultLev); - } + if (row.generics.length === 0) { return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1; + } else if (row.generics.length > 0 && row.generics[0].name === null) { + return checkGenerics(row.generics[0], elem, defaultLev); } // The names match, but we need to be sure that all generics kinda // match as well. let elem_name; - if (elem.generics.length > 0 && row[GENERICS_DATA].length >= elem.generics.length) { + if (elem.generics.length > 0 && row.generics.length >= elem.generics.length) { const elems = Object.create(null); - for (const entry of row[GENERICS_DATA]) { - elem_name = entry[NAME]; + for (const entry of row.generics) { + elem_name = entry.name; if (elem_name === "") { // Pure generic, needs to check into it. if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) { @@ -963,7 +956,7 @@ function initSearch(rawSearchIndex) { */ function checkIfInGenerics(row, elem) { let lev = MAX_LEV_DISTANCE + 1; - for (const entry of row[GENERICS_DATA]) { + for (const entry of row.generics) { lev = Math.min(checkType(entry, elem, true), lev); if (lev === 0) { break; @@ -984,23 +977,22 @@ function initSearch(rawSearchIndex) { * no match, returns `MAX_LEV_DISTANCE + 1`. */ function checkType(row, elem, literalSearch) { - if (row[NAME].length === 0) { + if (row.name === null) { // This is a pure "generic" search, no need to run other checks. - if (row.length > GENERICS_DATA) { + if (row.generics.length > 0) { return checkIfInGenerics(row, elem); } return MAX_LEV_DISTANCE + 1; } - let lev = levenshtein(row[NAME], elem.name); + let lev = levenshtein(row.name, elem.name); if (literalSearch) { if (lev !== 0) { // The name didn't match, let's try to check if the generics do. if (elem.generics.length === 0) { - const checkGeneric = (row.length > GENERICS_DATA && - row[GENERICS_DATA].length > 0); - if (checkGeneric && row[GENERICS_DATA] - .findIndex(tmp_elem => tmp_elem[NAME] === elem.name) !== -1) { + const checkGeneric = row.generics.length > 0; + if (checkGeneric && row.generics + .findIndex(tmp_elem => tmp_elem.name === elem.name) !== -1) { return 0; } } @@ -1009,7 +1001,7 @@ function initSearch(rawSearchIndex) { return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1); } return 0; - } else if (row.length > GENERICS_DATA) { + } else if (row.generics.length > 0) { if (elem.generics.length === 0) { if (lev === 0) { return 0; @@ -1059,9 +1051,9 @@ function initSearch(rawSearchIndex) { function findArg(row, elem, typeFilter) { let lev = MAX_LEV_DISTANCE + 1; - if (row && row.type && row.type[INPUTS_DATA] && row.type[INPUTS_DATA].length > 0) { - for (const input of row.type[INPUTS_DATA]) { - if (!typePassesFilter(typeFilter, input[1])) { + if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { + for (const input of row.type.inputs) { + if (!typePassesFilter(typeFilter, input.ty)) { continue; } lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch)); @@ -1086,13 +1078,10 @@ function initSearch(rawSearchIndex) { function checkReturned(row, elem, typeFilter) { let lev = MAX_LEV_DISTANCE + 1; - if (row && row.type && row.type.length > OUTPUT_DATA) { - let ret = row.type[OUTPUT_DATA]; - if (typeof ret[0] === "string") { - ret = [ret]; - } + if (row && row.type && row.type.output.length > 0) { + const ret = row.type.output; for (const ret_ty of ret) { - if (!typePassesFilter(typeFilter, ret_ty[1])) { + if (!typePassesFilter(typeFilter, ret_ty.ty)) { continue; } lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch)); @@ -1836,6 +1825,97 @@ 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<RawFunctionType>} types + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {Array<FunctionSearchType>} + */ + function buildItemSearchTypeAll(types, lowercasePaths) { + const PATH_INDEX_DATA = 0; + const GENERICS_DATA = 1; + return types.map(type => { + let pathIndex, generics; + if (typeof type === "number") { + pathIndex = type; + generics = []; + } else { + pathIndex = type[PATH_INDEX_DATA]; + generics = buildItemSearchTypeAll(type[GENERICS_DATA], lowercasePaths); + } + return { + // `0` is used as a sentinel because it's fewer bytes than `null` + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: generics, + }; + }); + } + + /** + * 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; + // `0` is used as a sentinel because it's fewer bytes than `null` + if (functionSearchType === 0) { + return null; + } + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + const pathIndex = functionSearchType[INPUTS_DATA]; + inputs = [{ + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: [], + }]; + } else { + inputs = buildItemSearchTypeAll(functionSearchType[INPUTS_DATA], lowercasePaths); + } + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + const pathIndex = functionSearchType[OUTPUT_DATA]; + output = [{ + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: [], + }]; + } else { + output = buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA], lowercasePaths); + } + } else { + output = []; + } + return { + inputs, output, + }; + } + function buildIndex(rawSearchIndex) { searchIndex = []; /** @@ -1862,14 +1942,22 @@ function initSearch(rawSearchIndex) { * 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. + * i[i] contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * f[i] contains function signatures, or `0` if the item isn't a function. + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. * * `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. + * `p` is a list of path/type pairs. It is used for parents and function parameters. * * @type {{ * doc: string, @@ -1879,7 +1967,7 @@ function initSearch(rawSearchIndex) { * d: Array<string>, * q: Array<string>, * i: Array<Number>, - * f: Array<Array<?>>, + * f: Array<RawFunctionSearchType>, * p: Array<Object>, * }} */ @@ -1923,9 +2011,14 @@ function initSearch(rawSearchIndex) { // [Number] index to items] const aliases = crateCorpus.a; + // an array of [{name: String, ty: Number}] + const lowercasePaths = []; + // convert `rawPaths` entries into object form + // generate normalizedPaths for function search mode let len = paths.length; for (i = 0; i < len; ++i) { + lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()}); paths[i] = {ty: paths[i][0], name: paths[i][1]}; } @@ -1955,7 +2048,7 @@ function initSearch(rawSearchIndex) { path: itemPaths[i] ? itemPaths[i] : lastPath, desc: itemDescs[i], parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, - type: itemFunctionSearchTypes[i], + type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths), id: id, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), }; diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 290c29d3141..acb1d8d7b5c 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -10,6 +10,7 @@ (function() { const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value; +let oldScrollPosition = 0; function createDirEntry(elem, parent, fullPath, hasFoundFile) { const name = document.createElement("div"); @@ -65,10 +66,24 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) { function toggleSidebar() { const child = this.children[0]; if (child.innerText === ">") { + if (window.innerWidth < 701) { + // This is to keep the scroll position on mobile. + oldScrollPosition = window.scrollY; + document.body.style.position = "fixed"; + document.body.style.top = `-${oldScrollPosition}px`; + } addClass(document.documentElement, "source-sidebar-expanded"); child.innerText = "<"; updateLocalStorage("source-sidebar-show", "true"); } else { + if (window.innerWidth < 701) { + // This is to keep the scroll position on mobile. + document.body.style.position = ""; + document.body.style.top = ""; + // The scroll position is lost when resetting the style, hence why we store it in + // `oldScroll`. + window.scrollTo(0, oldScrollPosition); + } removeClass(document.documentElement, "source-sidebar-expanded"); child.innerText = ">"; updateLocalStorage("source-sidebar-show", "false"); |
