From c23a457e72abe608715ac76f076f47dc42af07a5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 30 May 2024 20:31:44 +0200 Subject: Merging upstream version 1.74.1+dfsg1. Signed-off-by: Daniel Baumann --- src/librustdoc/html/static/css/noscript.css | 213 +++++++ src/librustdoc/html/static/css/rustdoc.css | 512 +++++++++++++++- src/librustdoc/html/static/css/settings.css | 63 -- src/librustdoc/html/static/css/themes/ayu.css | 180 ------ src/librustdoc/html/static/css/themes/dark.css | 101 ---- src/librustdoc/html/static/css/themes/light.css | 98 --- src/librustdoc/html/static/js/externs.js | 54 +- src/librustdoc/html/static/js/main.js | 49 +- src/librustdoc/html/static/js/search.js | 752 ++++++++++++++++-------- src/librustdoc/html/static/js/settings.js | 8 +- src/librustdoc/html/static/js/storage.js | 36 +- 11 files changed, 1312 insertions(+), 754 deletions(-) delete mode 100644 src/librustdoc/html/static/css/settings.css delete mode 100644 src/librustdoc/html/static/css/themes/ayu.css delete mode 100644 src/librustdoc/html/static/css/themes/dark.css delete mode 100644 src/librustdoc/html/static/css/themes/light.css (limited to 'src/librustdoc/html/static') diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 93aa11a58..fe0cf6dc8 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -28,3 +28,216 @@ nav.sub { https://github.com/rust-lang/rust/issues/102576 */ display: none; } + +/* Begin: styles for themes + Keep the default light and dark themes synchronized with the ones + in rustdoc.css */ + +/* Begin theme: light */ +:root { + --main-background-color: white; + --main-color: black; + --settings-input-color: #2196f3; + --settings-input-border-color: #717171; + --settings-button-color: #000; + --settings-button-border-focus: #717171; + --sidebar-background-color: #f5f5f5; + --sidebar-background-color-hover: #e0e0e0; + --code-block-background-color: #f5f5f5; + --scrollbar-track-background-color: #dcdcdc; + --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6); + --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; + --headings-border-bottom-color: #ddd; + --border-color: #e0e0e0; + --button-background-color: #fff; + --right-side-color: grey; + --code-attribute-color: #999; + --toggles-color: #999; + --toggle-filter: none; + --search-input-focused-border-color: #66afe9; + --copy-path-button-color: #999; + --copy-path-img-filter: invert(50%); + --copy-path-img-hover-filter: invert(35%); + --codeblock-error-hover-color: rgb(255, 0, 0); + --codeblock-error-color: rgba(255, 0, 0, .5); + --codeblock-ignore-hover-color: rgb(255, 142, 0); + --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: #ff8e00; + --type-link-color: #ad378a; + --trait-link-color: #6e4fc9; + --assoc-item-link-color: #3873ad; + --function-link-color: #ad7c37; + --macro-link-color: #068000; + --keyword-link-color: #3873ad; + --mod-link-color: #3873ad; + --link-color: #3873ad; + --sidebar-link-color: #356da4; + --sidebar-current-link-background-color: #fff; + --search-result-link-focus-background-color: #ccc; + --search-result-border-color: #aaa3; + --search-color: #000; + --search-error-code-background-color: #d0cccc; + --search-results-alias-color: #000; + --search-results-grey-color: #999; + --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #e6e6e6; + --search-tab-button-not-selected-background: #e6e6e6; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #fff; + --stab-background-color: #fff5d6; + --stab-code-color: #000; + --code-highlight-kw-color: #8959a8; + --code-highlight-kw-2-color: #4271ae; + --code-highlight-lifetime-color: #b76514; + --code-highlight-prelude-color: #4271ae; + --code-highlight-prelude-val-color: #c82829; + --code-highlight-number-color: #718c00; + --code-highlight-string-color: #718c00; + --code-highlight-literal-color: #c82829; + --code-highlight-attribute-color: #c82829; + --code-highlight-self-color: #c82829; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8e908c; + --code-highlight-doc-comment-color: #4d4d4c; + --src-line-numbers-span-color: #c67e2d; + --src-line-number-highlighted-background-color: #fdffd3; + --test-arrow-color: #f5f5f5; + --test-arrow-background-color: rgba(78, 139, 202, 0.2); + --test-arrow-hover-color: #f5f5f5; + --test-arrow-hover-background-color: rgb(78, 139, 202); + --target-background-color: #fdffd3; + --target-border-color: #ad7c37; + --kbd-color: #000; + --kbd-background: #fafbfc; + --kbd-box-shadow-color: #c6cbd1; + --rust-logo-filter: initial; + /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ + --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) + brightness(114%) contrast(76%); + --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) + brightness(96%) contrast(93%); + --crate-search-hover-border: #717171; + --src-sidebar-background-selected: #fff; + --src-sidebar-background-hover: #e0e0e0; + --table-alt-row-background-color: #f5f5f5; + --codeblock-link-background: #eee; + --scrape-example-toggle-line-background: #ccc; + --scrape-example-toggle-line-hover-background: #999; + --scrape-example-code-line-highlight: #fcffd6; + --scrape-example-code-line-highlight-focus: #f6fdb0; + --scrape-example-help-border-color: #555; + --scrape-example-help-color: #333; + --scrape-example-help-hover-border-color: #000; + --scrape-example-help-hover-color: #000; + --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1); + --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); +} +/* End theme: light */ + +@media (prefers-color-scheme: dark) { + /* Begin theme: dark */ + :root { + --main-background-color: #353535; + --main-color: #ddd; + --settings-input-color: #2196f3; + --settings-input-border-color: #999; + --settings-button-color: #000; + --settings-button-border-focus: #ffb900; + --sidebar-background-color: #505050; + --sidebar-background-color-hover: #676767; + --code-block-background-color: #2A2A2A; + --scrollbar-track-background-color: #717171; + --scrollbar-thumb-background-color: rgba(32, 34, 37, .6); + --scrollbar-color: rgba(32,34,37,.6) #5a5a5a; + --headings-border-bottom-color: #d2d2d2; + --border-color: #e0e0e0; + --button-background-color: #f0f0f0; + --right-side-color: grey; + --code-attribute-color: #999; + --toggles-color: #999; + --toggle-filter: invert(100%); + --search-input-focused-border-color: #008dfd; + --copy-path-button-color: #999; + --copy-path-img-filter: invert(50%); + --copy-path-img-hover-filter: invert(65%); + --codeblock-error-hover-color: rgb(255, 0, 0); + --codeblock-error-color: rgba(255, 0, 0, .5); + --codeblock-ignore-hover-color: rgb(255, 142, 0); + --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: #ff8e00; + --type-link-color: #2dbfb8; + --trait-link-color: #b78cf2; + --assoc-item-link-color: #d2991d; + --function-link-color: #2bab63; + --macro-link-color: #09bd00; + --keyword-link-color: #d2991d; + --mod-link-color: #d2991d; + --link-color: #d2991d; + --sidebar-link-color: #fdbf35; + --sidebar-current-link-background-color: #444; + --search-result-link-focus-background-color: #616161; + --search-result-border-color: #aaa3; + --search-color: #111; + --search-error-code-background-color: #484848; + --search-results-alias-color: #fff; + --search-results-grey-color: #ccc; + --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #252525; + --search-tab-button-not-selected-background: #252525; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #353535; + --stab-background-color: #314559; + --stab-code-color: #e6e1cf; + --code-highlight-kw-color: #ab8ac1; + --code-highlight-kw-2-color: #769acb; + --code-highlight-lifetime-color: #d97f26; + --code-highlight-prelude-color: #769acb; + --code-highlight-prelude-val-color: #ee6868; + --code-highlight-number-color: #83a300; + --code-highlight-string-color: #83a300; + --code-highlight-literal-color: #ee6868; + --code-highlight-attribute-color: #ee6868; + --code-highlight-self-color: #ee6868; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8d8d8b; + --code-highlight-doc-comment-color: #8ca375; + --src-line-numbers-span-color: #3b91e2; + --src-line-number-highlighted-background-color: #0a042f; + --test-arrow-color: #dedede; + --test-arrow-background-color: rgba(78, 139, 202, 0.2); + --test-arrow-hover-color: #dedede; + --test-arrow-hover-background-color: #4e8bca; + --target-background-color: #494a3d; + --target-border-color: #bb7410; + --kbd-color: #000; + --kbd-background: #fafbfc; + --kbd-box-shadow-color: #c6cbd1; + --rust-logo-filter: drop-shadow(1px 0 0px #fff) + drop-shadow(0 1px 0 #fff) + drop-shadow(-1px 0 0 #fff) + drop-shadow(0 -1px 0 #fff); + /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ + --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) + brightness(90%) contrast(90%); + --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) + brightness(100%) contrast(91%); + --crate-search-hover-border: #2196f3; + --src-sidebar-background-selected: #333; + --src-sidebar-background-hover: #444; + --table-alt-row-background-color: #2a2a2a; + --codeblock-link-background: #333; + --scrape-example-toggle-line-background: #999; + --scrape-example-toggle-line-hover-background: #c5c5c5; + --scrape-example-code-line-highlight: #5b3b01; + --scrape-example-code-line-highlight-focus: #7c4b0f; + --scrape-example-help-border-color: #aaa; + --scrape-example-help-color: #eee; + --scrape-example-help-hover-border-color: #fff; + --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1); + --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); + } +/* End theme: dark */ +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b1de8c152..47f9e6502 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -270,7 +270,7 @@ ul ul, ol ul, ul ol, ol ol { margin-bottom: .625em; } -p { +p, .docblock > .warning { /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */ @@ -278,7 +278,7 @@ p { } /* For the last child of a div, the margin will be taken care of by the margin-top of the next item. */ -p:last-child { +p:last-child, .docblock > .warning:last-child { margin: 0; } @@ -925,6 +925,70 @@ so that we can apply CSS-filters to change the arrow color in themes */ top: -5px; } +.setting-line { + margin: 1.2em 0.6em; +} + +.setting-radio input, .setting-check input { + margin-right: 0.3em; + height: 1.2rem; + width: 1.2rem; + border: 2px solid var(--settings-input-border-color); + outline: none; + -webkit-appearance: none; + cursor: pointer; +} +.setting-radio input { + border-radius: 50%; +} + +.setting-radio span, .setting-check span { + padding-bottom: 1px; +} + +.setting-radio { + margin-top: 0.1em; + margin-bottom: 0.1em; + min-width: 3.8em; + padding: 0.3em; + display: inline-flex; + align-items: center; + cursor: pointer; +} +.setting-radio + .setting-radio { + margin-left: 0.5em; +} + +.setting-check { + margin-right: 20px; + display: flex; + align-items: center; + cursor: pointer; +} + +.setting-radio input:checked { + box-shadow: inset 0 0 0 3px var(--main-background-color); + background-color: var(--settings-input-color); +} +.setting-check input:checked { + background-color: var(--settings-input-color); + border-width: 1px; + content: url('data:image/svg+xml,\ + \ + '); +} +.setting-radio input:focus, .setting-check input:focus { + box-shadow: 0 0 1px 1px var(--settings-input-color); +} +/* In here we combine both `:focus` and `:checked` properties. */ +.setting-radio input:checked:focus { + box-shadow: inset 0 0 0 3px var(--main-background-color), + 0 0 2px 2px var(--settings-input-color); +} +.setting-radio input:hover, .setting-check input:hover { + border-color: var(--settings-input-color) !important; +} + /* use larger max-width for help popover, but not for help.html */ #help.popover { max-width: 600px; @@ -1096,7 +1160,7 @@ pre.rust .doccomment { } .example-wrap.ignore .tooltip { - color: var(--codeblock-ignore-color); + color: var(--codeblock-ignore-color); } .example-wrap.compile_fail:hover .tooltip, @@ -1124,6 +1188,26 @@ pre.rust .doccomment { font-size: 1.25rem; } +/* This class only exists for users who want to draw attention to a particular element in their +documentation. */ +.content .docblock .warning { + border-left: 2px solid var(--warning-border-color); + padding: 14px; + position: relative; + /* The "!important" part is required because the rule is otherwise overruled in this CSS + selector: ".docblock > :not(.more-examples-toggle):not(.example-wrap)" */ + overflow-x: visible !important; +} +.content .docblock .warning::before { + color: var(--warning-border-color); + content: "ⓘ"; + position: absolute; + left: -25px; + top: 5px; + font-weight: bold; + font-size: 1.25rem; +} + a.test-arrow { visibility: hidden; position: absolute; @@ -1271,6 +1355,7 @@ a.tooltip:hover::after { #search-tabs .count { font-size: 1rem; + font-variant-numeric: tabular-nums; color: var(--search-tab-title-count-color); } @@ -1553,6 +1638,13 @@ However, it's not needed with smaller screen width because the doc/code block is /* Media Queries */ +/* Make sure all the buttons line wrap at the same time */ +@media (max-width: 850px) { + #search-tabs .count { + display: block; + } +} + /* WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY If you update this line, then you also need to update the line with the same warning @@ -1680,10 +1772,6 @@ in src-script.js display: none !important; } - #search-tabs .count { - display: block; - } - #main-content > details.toggle > summary::before, #main-content > div > details.toggle > summary::before { left: -11px; @@ -2014,3 +2102,413 @@ in src-script.js } /* End: styles for --scrape-examples feature */ + +/* Begin: styles for themes + + Keep the default light and dark themes synchronized with the ones + in noscript.css + + The special "Begin theme" and "End theme" below are used by a lot of + tooling to ensure different themes all define all the variables. Do not + alter their formatting. */ + +/* Begin theme: light */ +:root[data-theme="light"] { + --main-background-color: white; + --main-color: black; + --settings-input-color: #2196f3; + --settings-input-border-color: #717171; + --settings-button-color: #000; + --settings-button-border-focus: #717171; + --sidebar-background-color: #f5f5f5; + --sidebar-background-color-hover: #e0e0e0; + --code-block-background-color: #f5f5f5; + --scrollbar-track-background-color: #dcdcdc; + --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6); + --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; + --headings-border-bottom-color: #ddd; + --border-color: #e0e0e0; + --button-background-color: #fff; + --right-side-color: grey; + --code-attribute-color: #999; + --toggles-color: #999; + --toggle-filter: none; + --search-input-focused-border-color: #66afe9; + --copy-path-button-color: #999; + --copy-path-img-filter: invert(50%); + --copy-path-img-hover-filter: invert(35%); + --codeblock-error-hover-color: rgb(255, 0, 0); + --codeblock-error-color: rgba(255, 0, 0, .5); + --codeblock-ignore-hover-color: rgb(255, 142, 0); + --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: #ff8e00; + --type-link-color: #ad378a; + --trait-link-color: #6e4fc9; + --assoc-item-link-color: #3873ad; + --function-link-color: #ad7c37; + --macro-link-color: #068000; + --keyword-link-color: #3873ad; + --mod-link-color: #3873ad; + --link-color: #3873ad; + --sidebar-link-color: #356da4; + --sidebar-current-link-background-color: #fff; + --search-result-link-focus-background-color: #ccc; + --search-result-border-color: #aaa3; + --search-color: #000; + --search-error-code-background-color: #d0cccc; + --search-results-alias-color: #000; + --search-results-grey-color: #999; + --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #e6e6e6; + --search-tab-button-not-selected-background: #e6e6e6; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #fff; + --stab-background-color: #fff5d6; + --stab-code-color: #000; + --code-highlight-kw-color: #8959a8; + --code-highlight-kw-2-color: #4271ae; + --code-highlight-lifetime-color: #b76514; + --code-highlight-prelude-color: #4271ae; + --code-highlight-prelude-val-color: #c82829; + --code-highlight-number-color: #718c00; + --code-highlight-string-color: #718c00; + --code-highlight-literal-color: #c82829; + --code-highlight-attribute-color: #c82829; + --code-highlight-self-color: #c82829; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8e908c; + --code-highlight-doc-comment-color: #4d4d4c; + --src-line-numbers-span-color: #c67e2d; + --src-line-number-highlighted-background-color: #fdffd3; + --test-arrow-color: #f5f5f5; + --test-arrow-background-color: rgba(78, 139, 202, 0.2); + --test-arrow-hover-color: #f5f5f5; + --test-arrow-hover-background-color: rgb(78, 139, 202); + --target-background-color: #fdffd3; + --target-border-color: #ad7c37; + --kbd-color: #000; + --kbd-background: #fafbfc; + --kbd-box-shadow-color: #c6cbd1; + --rust-logo-filter: initial; + /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ + --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) + brightness(114%) contrast(76%); + --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) + brightness(96%) contrast(93%); + --crate-search-hover-border: #717171; + --src-sidebar-background-selected: #fff; + --src-sidebar-background-hover: #e0e0e0; + --table-alt-row-background-color: #f5f5f5; + --codeblock-link-background: #eee; + --scrape-example-toggle-line-background: #ccc; + --scrape-example-toggle-line-hover-background: #999; + --scrape-example-code-line-highlight: #fcffd6; + --scrape-example-code-line-highlight-focus: #f6fdb0; + --scrape-example-help-border-color: #555; + --scrape-example-help-color: #333; + --scrape-example-help-hover-border-color: #000; + --scrape-example-help-hover-color: #000; + --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1); + --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); +} +/* End theme: light */ + +/* Begin theme: dark */ +:root[data-theme="dark"] { + --main-background-color: #353535; + --main-color: #ddd; + --settings-input-color: #2196f3; + --settings-input-border-color: #999; + --settings-button-color: #000; + --settings-button-border-focus: #ffb900; + --sidebar-background-color: #505050; + --sidebar-background-color-hover: #676767; + --code-block-background-color: #2A2A2A; + --scrollbar-track-background-color: #717171; + --scrollbar-thumb-background-color: rgba(32, 34, 37, .6); + --scrollbar-color: rgba(32,34,37,.6) #5a5a5a; + --headings-border-bottom-color: #d2d2d2; + --border-color: #e0e0e0; + --button-background-color: #f0f0f0; + --right-side-color: grey; + --code-attribute-color: #999; + --toggles-color: #999; + --toggle-filter: invert(100%); + --search-input-focused-border-color: #008dfd; + --copy-path-button-color: #999; + --copy-path-img-filter: invert(50%); + --copy-path-img-hover-filter: invert(65%); + --codeblock-error-hover-color: rgb(255, 0, 0); + --codeblock-error-color: rgba(255, 0, 0, .5); + --codeblock-ignore-hover-color: rgb(255, 142, 0); + --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: #ff8e00; + --type-link-color: #2dbfb8; + --trait-link-color: #b78cf2; + --assoc-item-link-color: #d2991d; + --function-link-color: #2bab63; + --macro-link-color: #09bd00; + --keyword-link-color: #d2991d; + --mod-link-color: #d2991d; + --link-color: #d2991d; + --sidebar-link-color: #fdbf35; + --sidebar-current-link-background-color: #444; + --search-result-link-focus-background-color: #616161; + --search-result-border-color: #aaa3; + --search-color: #111; + --search-error-code-background-color: #484848; + --search-results-alias-color: #fff; + --search-results-grey-color: #ccc; + --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #252525; + --search-tab-button-not-selected-background: #252525; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #353535; + --stab-background-color: #314559; + --stab-code-color: #e6e1cf; + --code-highlight-kw-color: #ab8ac1; + --code-highlight-kw-2-color: #769acb; + --code-highlight-lifetime-color: #d97f26; + --code-highlight-prelude-color: #769acb; + --code-highlight-prelude-val-color: #ee6868; + --code-highlight-number-color: #83a300; + --code-highlight-string-color: #83a300; + --code-highlight-literal-color: #ee6868; + --code-highlight-attribute-color: #ee6868; + --code-highlight-self-color: #ee6868; + --code-highlight-macro-color: #3e999f; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #8d8d8b; + --code-highlight-doc-comment-color: #8ca375; + --src-line-numbers-span-color: #3b91e2; + --src-line-number-highlighted-background-color: #0a042f; + --test-arrow-color: #dedede; + --test-arrow-background-color: rgba(78, 139, 202, 0.2); + --test-arrow-hover-color: #dedede; + --test-arrow-hover-background-color: #4e8bca; + --target-background-color: #494a3d; + --target-border-color: #bb7410; + --kbd-color: #000; + --kbd-background: #fafbfc; + --kbd-box-shadow-color: #c6cbd1; + --rust-logo-filter: drop-shadow(1px 0 0px #fff) + drop-shadow(0 1px 0 #fff) + drop-shadow(-1px 0 0 #fff) + drop-shadow(0 -1px 0 #fff); + /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ + --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) + brightness(90%) contrast(90%); + --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) + brightness(100%) contrast(91%); + --crate-search-hover-border: #2196f3; + --src-sidebar-background-selected: #333; + --src-sidebar-background-hover: #444; + --table-alt-row-background-color: #2a2a2a; + --codeblock-link-background: #333; + --scrape-example-toggle-line-background: #999; + --scrape-example-toggle-line-hover-background: #c5c5c5; + --scrape-example-code-line-highlight: #5b3b01; + --scrape-example-code-line-highlight-focus: #7c4b0f; + --scrape-example-help-border-color: #aaa; + --scrape-example-help-color: #eee; + --scrape-example-help-hover-border-color: #fff; + --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1); + --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); +} +/* End theme: dark */ + +/* Begin theme: ayu */ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ +:root[data-theme="ayu"] { + --main-background-color: #0f1419; + --main-color: #c5c5c5; + --settings-input-color: #ffb454; + --settings-input-border-color: #999; + --settings-button-color: #fff; + --settings-button-border-focus: #e0e0e0; + --sidebar-background-color: #14191f; + --sidebar-background-color-hover: rgba(70, 70, 70, 0.33); + --code-block-background-color: #191f26; + --scrollbar-track-background-color: transparent; + --scrollbar-thumb-background-color: #5c6773; + --scrollbar-color: #5c6773 #24292f; + --headings-border-bottom-color: #5c6773; + --border-color: #5c6773; + --button-background-color: #141920; + --right-side-color: grey; + --code-attribute-color: #999; + --toggles-color: #999; + --toggle-filter: invert(100%); + --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */ + --copy-path-button-color: #fff; + --copy-path-img-filter: invert(70%); + --copy-path-img-hover-filter: invert(100%); + --codeblock-error-hover-color: rgb(255, 0, 0); + --codeblock-error-color: rgba(255, 0, 0, .5); + --codeblock-ignore-hover-color: rgb(255, 142, 0); + --codeblock-ignore-color: rgba(255, 142, 0, .6); + --warning-border-color: #ff8e00; + --type-link-color: #ffa0a5; + --trait-link-color: #39afd7; + --assoc-item-link-color: #39afd7; + --function-link-color: #fdd687; + --macro-link-color: #a37acc; + --keyword-link-color: #39afd7; + --mod-link-color: #39afd7; + --link-color: #39afd7; + --sidebar-link-color: #53b1db; + --sidebar-current-link-background-color: transparent; + --search-result-link-focus-background-color: #3c3c3c; + --search-result-border-color: #aaa3; + --search-color: #fff; + --search-error-code-background-color: #4f4c4c; + --search-results-alias-color: #c5c5c5; + --search-results-grey-color: #999; + --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: none; + --search-tab-button-not-selected-background: transparent !important; + --search-tab-button-selected-border-top-color: none; + --search-tab-button-selected-background: #141920 !important; + --stab-background-color: #314559; + --stab-code-color: #e6e1cf; + --code-highlight-kw-color: #ff7733; + --code-highlight-kw-2-color: #ff7733; + --code-highlight-lifetime-color: #ff7733; + --code-highlight-prelude-color: #69f2df; + --code-highlight-prelude-val-color: #ff7733; + --code-highlight-number-color: #b8cc52; + --code-highlight-string-color: #b8cc52; + --code-highlight-literal-color: #ff7733; + --code-highlight-attribute-color: #e6e1cf; + --code-highlight-self-color: #36a3d9; + --code-highlight-macro-color: #a37acc; + --code-highlight-question-mark-color: #ff9011; + --code-highlight-comment-color: #788797; + --code-highlight-doc-comment-color: #a1ac88; + --src-line-numbers-span-color: #5c6773; + --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06); + --test-arrow-color: #788797; + --test-arrow-background-color: rgba(57, 175, 215, 0.09); + --test-arrow-hover-color: #c5c5c5; + --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368); + --target-background-color: rgba(255, 236, 164, 0.06); + --target-border-color: rgba(255, 180, 76, 0.85); + --kbd-color: #c5c5c5; + --kbd-background: #314559; + --kbd-box-shadow-color: #5c6773; + --rust-logo-filter: drop-shadow(1px 0 0px #fff) + drop-shadow(0 1px 0 #fff) + drop-shadow(-1px 0 0 #fff) + drop-shadow(0 -1px 0 #fff); + /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ + --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) + brightness(94%) contrast(94%); + --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) + brightness(113%) contrast(76%); + --crate-search-hover-border: #e0e0e0; + --src-sidebar-background-selected: #14191f; + --src-sidebar-background-hover: #14191f; + --table-alt-row-background-color: #191f26; + --codeblock-link-background: #333; + --scrape-example-toggle-line-background: #999; + --scrape-example-toggle-line-hover-background: #c5c5c5; + --scrape-example-code-line-highlight: #5b3b01; + --scrape-example-code-line-highlight-focus: #7c4b0f; + --scrape-example-help-border-color: #aaa; + --scrape-example-help-color: #eee; + --scrape-example-help-hover-border-color: #fff; + --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1); + --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); +} + +:root[data-theme="ayu"] h1, +:root[data-theme="ayu"] h2, +:root[data-theme="ayu"] h3, +:root[data-theme="ayu"] h4, +:where(:root[data-theme="ayu"]) h1 a, +:root[data-theme="ayu"] .sidebar h2 a, +:root[data-theme="ayu"] .sidebar h3 a, +:root[data-theme="ayu"] #source-sidebar > .title { + color: #fff; +} + +:root[data-theme="ayu"] .docblock code { + color: #ffb454; +} + +:root[data-theme="ayu"] .docblock a > code { + color: #39AFD7 !important; +} + +:root[data-theme="ayu"] .code-header, +:root[data-theme="ayu"] .docblock pre > code, +:root[data-theme="ayu"] pre, +:root[data-theme="ayu"] pre > code, +:root[data-theme="ayu"] .item-info code, +:root[data-theme="ayu"] .rustdoc.source .example-wrap { + color: #e6e1cf; +} + +:root[data-theme="ayu"] .sidebar .current, +:root[data-theme="ayu"] .sidebar a:hover, +:root[data-theme="ayu"] #src-sidebar div.files > a:hover, +:root[data-theme="ayu"] details.dir-entry summary:hover, +:root[data-theme="ayu"] #src-sidebar div.files > a:focus, +:root[data-theme="ayu"] details.dir-entry summary:focus, +:root[data-theme="ayu"] #src-sidebar div.files > a.selected { + color: #ffb44c; +} + +:root[data-theme="ayu"] .sidebar-elems .location { + color: #ff7733; +} + +:root[data-theme="ayu"] .src-line-numbers .line-highlighted { + color: #708090; + padding-right: 7px; + border-right: 1px solid #ffb44c; +} + +:root[data-theme="ayu"] .search-results a:hover, +:root[data-theme="ayu"] .search-results a:focus { + color: #fff !important; + background-color: #3c3c3c; +} + +:root[data-theme="ayu"] .search-results a { + color: #0096cf; +} + +:root[data-theme="ayu"] .search-results a div.desc { + color: #c5c5c5; +} + +:root[data-theme="ayu"] .result-name .primitive > i, +:root[data-theme="ayu"] .result-name .keyword > i { + color: #788797; +} + +:root[data-theme="ayu"] #search-tabs > button.selected { + border-bottom: 1px solid #ffb44c !important; + border-top: none; +} +:root[data-theme="ayu"] #search-tabs > button:not(.selected) { + border: none; + background-color: transparent !important; +} +:root[data-theme="ayu"] #search-tabs > button:hover { + border-bottom: 1px solid rgba(242, 151, 24, 0.3); +} + +:root[data-theme="ayu"] #settings-menu > a img { + filter: invert(100); +} +/* End theme: ayu */ + +/* End: styles for themes */ diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css deleted file mode 100644 index c1324c076..000000000 --- a/src/librustdoc/html/static/css/settings.css +++ /dev/null @@ -1,63 +0,0 @@ -.setting-line { - margin: 1.2em 0.6em; -} - -.setting-radio input, .setting-check input { - margin-right: 0.3em; - height: 1.2rem; - width: 1.2rem; - border: 2px solid var(--settings-input-border-color); - outline: none; - -webkit-appearance: none; - cursor: pointer; -} -.setting-radio input { - border-radius: 50%; -} - -.setting-radio span, .setting-check span { - padding-bottom: 1px; -} - -.setting-radio { - margin-top: 0.1em; - margin-bottom: 0.1em; - min-width: 3.8em; - padding: 0.3em; - display: inline-flex; - align-items: center; - cursor: pointer; -} -.setting-radio + .setting-radio { - margin-left: 0.5em; -} - -.setting-check { - margin-right: 20px; - display: flex; - align-items: center; - cursor: pointer; -} - -.setting-radio input:checked { - box-shadow: inset 0 0 0 3px var(--main-background-color); - background-color: var(--settings-input-color); -} -.setting-check input:checked { - background-color: var(--settings-input-color); - border-width: 1px; - content: url('data:image/svg+xml,\ - \ - '); -} -.setting-radio input:focus, .setting-check input:focus { - box-shadow: 0 0 1px 1px var(--settings-input-color); -} -/* In here we combine both `:focus` and `:checked` properties. */ -.setting-radio input:checked:focus { - box-shadow: inset 0 0 0 3px var(--main-background-color), - 0 0 2px 2px var(--settings-input-color); -} -.setting-radio input:hover, .setting-check input:hover { - border-color: var(--settings-input-color) !important; -} diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css deleted file mode 100644 index d8dae51eb..000000000 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ /dev/null @@ -1,180 +0,0 @@ -/* -Based off of the Ayu theme -Original by Dempfi (https://github.com/dempfi/ayu) -*/ - -:root { - --main-background-color: #0f1419; - --main-color: #c5c5c5; - --settings-input-color: #ffb454; - --settings-input-border-color: #999; - --settings-button-color: #fff; - --settings-button-border-focus: #e0e0e0; - --sidebar-background-color: #14191f; - --sidebar-background-color-hover: rgba(70, 70, 70, 0.33); - --code-block-background-color: #191f26; - --scrollbar-track-background-color: transparent; - --scrollbar-thumb-background-color: #5c6773; - --scrollbar-color: #5c6773 #24292f; - --headings-border-bottom-color: #5c6773; - --border-color: #5c6773; - --button-background-color: #141920; - --right-side-color: grey; - --code-attribute-color: #999; - --toggles-color: #999; - --toggle-filter: invert(100%); - --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */ - --copy-path-button-color: #fff; - --copy-path-img-filter: invert(70%); - --copy-path-img-hover-filter: invert(100%); - --codeblock-error-hover-color: rgb(255, 0, 0); - --codeblock-error-color: rgba(255, 0, 0, .5); - --codeblock-ignore-hover-color: rgb(255, 142, 0); - --codeblock-ignore-color: rgba(255, 142, 0, .6); - --type-link-color: #ffa0a5; - --trait-link-color: #39afd7; - --assoc-item-link-color: #39afd7; - --function-link-color: #fdd687; - --macro-link-color: #a37acc; - --keyword-link-color: #39afd7; - --mod-link-color: #39afd7; - --link-color: #39afd7; - --sidebar-link-color: #53b1db; - --sidebar-current-link-background-color: transparent; - --search-result-link-focus-background-color: #3c3c3c; - --search-result-border-color: #aaa3; - --search-color: #fff; - --search-error-code-background-color: #4f4c4c; - --search-results-alias-color: #c5c5c5; - --search-results-grey-color: #999; - --search-tab-title-count-color: #888; - --search-tab-button-not-selected-border-top-color: none; - --search-tab-button-not-selected-background: transparent !important; - --search-tab-button-selected-border-top-color: none; - --search-tab-button-selected-background: #141920 !important; - --stab-background-color: #314559; - --stab-code-color: #e6e1cf; - --code-highlight-kw-color: #ff7733; - --code-highlight-kw-2-color: #ff7733; - --code-highlight-lifetime-color: #ff7733; - --code-highlight-prelude-color: #69f2df; - --code-highlight-prelude-val-color: #ff7733; - --code-highlight-number-color: #b8cc52; - --code-highlight-string-color: #b8cc52; - --code-highlight-literal-color: #ff7733; - --code-highlight-attribute-color: #e6e1cf; - --code-highlight-self-color: #36a3d9; - --code-highlight-macro-color: #a37acc; - --code-highlight-question-mark-color: #ff9011; - --code-highlight-comment-color: #788797; - --code-highlight-doc-comment-color: #a1ac88; - --src-line-numbers-span-color: #5c6773; - --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06); - --test-arrow-color: #788797; - --test-arrow-background-color: rgba(57, 175, 215, 0.09); - --test-arrow-hover-color: #c5c5c5; - --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368); - --target-background-color: rgba(255, 236, 164, 0.06); - --target-border-color: rgba(255, 180, 76, 0.85); - --kbd-color: #c5c5c5; - --kbd-background: #314559; - --kbd-box-shadow-color: #5c6773; - --rust-logo-filter: drop-shadow(1px 0 0px #fff) - drop-shadow(0 1px 0 #fff) - drop-shadow(-1px 0 0 #fff) - drop-shadow(0 -1px 0 #fff); - /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ - --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) - brightness(94%) contrast(94%); - --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) - brightness(113%) contrast(76%); - --crate-search-hover-border: #e0e0e0; - --src-sidebar-background-selected: #14191f; - --src-sidebar-background-hover: #14191f; - --table-alt-row-background-color: #191f26; - --codeblock-link-background: #333; - --scrape-example-toggle-line-background: #999; - --scrape-example-toggle-line-hover-background: #c5c5c5; - --scrape-example-code-line-highlight: rgb(91, 59, 1); - --scrape-example-code-line-highlight-focus: rgb(124, 75, 15); - --scrape-example-help-border-color: #aaa; - --scrape-example-help-color: #eee; - --scrape-example-help-hover-border-color: #fff; - --scrape-example-help-hover-color: #fff; - --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1); - --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); -} - -h1, h2, h3, h4, -h1 a, .sidebar h2 a, .sidebar h3 a, -#src-sidebar > .title { - color: #fff; -} -h4 { - border: none; -} - -.docblock code { - color: #ffb454; -} -.docblock a > code { - color: #39AFD7 !important; -} -.code-header, -.docblock pre > code, -pre, pre > code, -.item-info code, -.rustdoc.src .example-wrap { - color: #e6e1cf; -} - -.sidebar .current, -.sidebar a:hover, -#src-sidebar div.files > a:hover, details.dir-entry summary:hover, -#src-sidebar div.files > a:focus, details.dir-entry summary:focus, -#src-sidebar div.files > a.selected { - color: #ffb44c; -} - -.sidebar-elems .location { - color: #ff7733; -} - -.src-line-numbers .line-highlighted { - color: #708090; - padding-right: 7px; - border-right: 1px solid #ffb44c; -} - -.search-results a:hover, -.search-results a:focus { - color: #fff !important; - background-color: #3c3c3c; -} - -.search-results a { - color: #0096cf; -} -.search-results a div.desc { - color: #c5c5c5; -} - -.result-name .primitive > i, .result-name .keyword > i { - color: #788797; -} - -#search-tabs > button.selected { - border-bottom: 1px solid #ffb44c !important; - border-top: none; -} -#search-tabs > button:not(.selected) { - border: none; - background-color: transparent !important; -} -#search-tabs > button:hover { - border-bottom: 1px solid rgba(242, 151, 24, 0.3); -} - -#settings-menu > a img { - filter: invert(100); -} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css deleted file mode 100644 index 2b3029887..000000000 --- a/src/librustdoc/html/static/css/themes/dark.css +++ /dev/null @@ -1,101 +0,0 @@ -:root { - --main-background-color: #353535; - --main-color: #ddd; - --settings-input-color: #2196f3; - --settings-input-border-color: #999; - --settings-button-color: #000; - --settings-button-border-focus: #ffb900; - --sidebar-background-color: #505050; - --sidebar-background-color-hover: #676767; - --code-block-background-color: #2A2A2A; - --scrollbar-track-background-color: #717171; - --scrollbar-thumb-background-color: rgba(32, 34, 37, .6); - --scrollbar-color: rgba(32,34,37,.6) #5a5a5a; - --headings-border-bottom-color: #d2d2d2; - --border-color: #e0e0e0; - --button-background-color: #f0f0f0; - --right-side-color: grey; - --code-attribute-color: #999; - --toggles-color: #999; - --toggle-filter: invert(100%); - --search-input-focused-border-color: #008dfd; - --copy-path-button-color: #999; - --copy-path-img-filter: invert(50%); - --copy-path-img-hover-filter: invert(65%); - --codeblock-error-hover-color: rgb(255, 0, 0); - --codeblock-error-color: rgba(255, 0, 0, .5); - --codeblock-ignore-hover-color: rgb(255, 142, 0); - --codeblock-ignore-color: rgba(255, 142, 0, .6); - --type-link-color: #2dbfb8; - --trait-link-color: #b78cf2; - --assoc-item-link-color: #d2991d; - --function-link-color: #2bab63; - --macro-link-color: #09bd00; - --keyword-link-color: #d2991d; - --mod-link-color: #d2991d; - --link-color: #d2991d; - --sidebar-link-color: #fdbf35; - --sidebar-current-link-background-color: #444; - --search-result-link-focus-background-color: #616161; - --search-result-border-color: #aaa3; - --search-color: #111; - --search-error-code-background-color: #484848; - --search-results-alias-color: #fff; - --search-results-grey-color: #ccc; - --search-tab-title-count-color: #888; - --search-tab-button-not-selected-border-top-color: #252525; - --search-tab-button-not-selected-background: #252525; - --search-tab-button-selected-border-top-color: #0089ff; - --search-tab-button-selected-background: #353535; - --stab-background-color: #314559; - --stab-code-color: #e6e1cf; - --code-highlight-kw-color: #ab8ac1; - --code-highlight-kw-2-color: #769acb; - --code-highlight-lifetime-color: #d97f26; - --code-highlight-prelude-color: #769acb; - --code-highlight-prelude-val-color: #ee6868; - --code-highlight-number-color: #83a300; - --code-highlight-string-color: #83a300; - --code-highlight-literal-color: #ee6868; - --code-highlight-attribute-color: #ee6868; - --code-highlight-self-color: #ee6868; - --code-highlight-macro-color: #3e999f; - --code-highlight-question-mark-color: #ff9011; - --code-highlight-comment-color: #8d8d8b; - --code-highlight-doc-comment-color: #8ca375; - --src-line-numbers-span-color: #3b91e2; - --src-line-number-highlighted-background-color: #0a042f; - --test-arrow-color: #dedede; - --test-arrow-background-color: rgba(78, 139, 202, 0.2); - --test-arrow-hover-color: #dedede; - --test-arrow-hover-background-color: rgb(78, 139, 202); - --target-background-color: #494a3d; - --target-border-color: #bb7410; - --kbd-color: #000; - --kbd-background: #fafbfc; - --kbd-box-shadow-color: #c6cbd1; - --rust-logo-filter: drop-shadow(1px 0 0px #fff) - drop-shadow(0 1px 0 #fff) - drop-shadow(-1px 0 0 #fff) - drop-shadow(0 -1px 0 #fff); - /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ - --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) - brightness(90%) contrast(90%); - --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) - brightness(100%) contrast(91%); - --crate-search-hover-border: #2196f3; - --src-sidebar-background-selected: #333; - --src-sidebar-background-hover: #444; - --table-alt-row-background-color: #2A2A2A; - --codeblock-link-background: #333; - --scrape-example-toggle-line-background: #999; - --scrape-example-toggle-line-hover-background: #c5c5c5; - --scrape-example-code-line-highlight: rgb(91, 59, 1); - --scrape-example-code-line-highlight-focus: rgb(124, 75, 15); - --scrape-example-help-border-color: #aaa; - --scrape-example-help-color: #eee; - --scrape-example-help-hover-border-color: #fff; - --scrape-example-help-hover-color: #fff; - --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1); - --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); -} diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css deleted file mode 100644 index 56fd8cbef..000000000 --- a/src/librustdoc/html/static/css/themes/light.css +++ /dev/null @@ -1,98 +0,0 @@ -:root { - --main-background-color: white; - --main-color: black; - --settings-input-color: #2196f3; - --settings-input-border-color: #717171; - --settings-button-color: #000; - --settings-button-border-focus: #717171; - --sidebar-background-color: #F5F5F5; - --sidebar-background-color-hover: #E0E0E0; - --code-block-background-color: #F5F5F5; - --scrollbar-track-background-color: #dcdcdc; - --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6); - --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; - --headings-border-bottom-color: #ddd; - --border-color: #e0e0e0; - --button-background-color: #fff; - --right-side-color: grey; - --code-attribute-color: #999; - --toggles-color: #999; - --toggle-filter: none; - --search-input-focused-border-color: #66afe9; - --copy-path-button-color: #999; - --copy-path-img-filter: invert(50%); - --copy-path-img-hover-filter: invert(35%); - --codeblock-error-hover-color: rgb(255, 0, 0); - --codeblock-error-color: rgba(255, 0, 0, .5); - --codeblock-ignore-hover-color: rgb(255, 142, 0); - --codeblock-ignore-color: rgba(255, 142, 0, .6); - --type-link-color: #ad378a; - --trait-link-color: #6e4fc9; - --assoc-item-link-color: #3873ad; - --function-link-color: #ad7c37; - --macro-link-color: #068000; - --keyword-link-color: #3873ad; - --mod-link-color: #3873ad; - --link-color: #3873ad; - --sidebar-link-color: #356da4; - --sidebar-current-link-background-color: #fff; - --search-result-link-focus-background-color: #ccc; - --search-result-border-color: #aaa3; - --search-color: #000; - --search-error-code-background-color: #d0cccc; - --search-results-alias-color: #000; - --search-results-grey-color: #999; - --search-tab-title-count-color: #888; - --search-tab-button-not-selected-border-top-color: #e6e6e6; - --search-tab-button-not-selected-background: #e6e6e6; - --search-tab-button-selected-border-top-color: #0089ff; - --search-tab-button-selected-background: #ffffff; - --stab-background-color: #fff5d6; - --stab-code-color: #000; - --code-highlight-kw-color: #8959a8; - --code-highlight-kw-2-color: #4271ae; - --code-highlight-lifetime-color: #b76514; - --code-highlight-prelude-color: #4271ae; - --code-highlight-prelude-val-color: #c82829; - --code-highlight-number-color: #718c00; - --code-highlight-string-color: #718c00; - --code-highlight-literal-color: #c82829; - --code-highlight-attribute-color: #c82829; - --code-highlight-self-color: #c82829; - --code-highlight-macro-color: #3e999f; - --code-highlight-question-mark-color: #ff9011; - --code-highlight-comment-color: #8e908c; - --code-highlight-doc-comment-color: #4d4d4c; - --src-line-numbers-span-color: #c67e2d; - --src-line-number-highlighted-background-color: #fdffd3; - --test-arrow-color: #f5f5f5; - --test-arrow-background-color: rgba(78, 139, 202, 0.2); - --test-arrow-hover-color: #f5f5f5; - --test-arrow-hover-background-color: rgb(78, 139, 202); - --target-background-color: #fdffd3; - --target-border-color: #ad7c37; - --kbd-color: #000; - --kbd-background: #fafbfc; - --kbd-box-shadow-color: #c6cbd1; - --rust-logo-filter: initial; - /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */ - --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) - brightness(114%) contrast(76%); - --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) - brightness(96%) contrast(93%); - --crate-search-hover-border: #717171; - --src-sidebar-background-selected: #fff; - --src-sidebar-background-hover: #e0e0e0; - --table-alt-row-background-color: #F5F5F5; - --codeblock-link-background: #eee; - --scrape-example-toggle-line-background: #ccc; - --scrape-example-toggle-line-hover-background: #999; - --scrape-example-code-line-highlight: #fcffd6; - --scrape-example-code-line-highlight-focus: #f6fdb0; - --scrape-example-help-border-color: #555; - --scrape-example-help-color: #333; - --scrape-example-help-hover-border-color: #000; - --scrape-example-help-hover-color: #000; - --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1); - --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); -} diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index f697abd07..c7811b43d 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -9,7 +9,7 @@ function initSearch(searchIndex){} /** * @typedef {{ * name: string, - * id: integer, + * id: integer|null, * fullPath: Array, * pathWithoutLast: Array, * pathLast: string, @@ -37,6 +37,7 @@ let ParserState; * args: Array, * returned: Array, * foundElems: number, + * totalElems: number, * literalSearch: boolean, * corrections: Array<{from: string, to: integer}>, * }} @@ -103,7 +104,7 @@ let ResultObject; * * fn something() -> Result * - * If output was allowed to be any RawFunctionType, it would look like this + * If output was allowed to be any RawFunctionType, it would look like thi * * [[], [50, [3, 3]]] * @@ -113,10 +114,56 @@ let ResultObject; * in favor of the pair of types interpretation. This is why the `(number|Array)` * is used instead of `(RawFunctionType|Array)`. * + * The output can be skipped if it's actually unit and there's no type constraints. If thi + * function accepts constrained generics, then the output will be unconditionally emitted, and + * after it will come a list of trait constraints. The position of the item in the list will + * determine which type parameter it is. For example: + * + * [1, 2, 3, 4, 5] + * ^ ^ ^ ^ ^ + * | | | | - generic parameter (-3) of trait 5 + * | | | - generic parameter (-2) of trait 4 + * | | - generic parameter (-1) of trait 3 + * | - this function returns a single value (type 2) + * - this function takes a single input parameter (type 1) + * + * Or, for a less contrived version: + * + * [[[4, -1], 3], [[5, -1]], 11] + * -^^^^^^^---- ^^^^^^^ ^^ + * | | | - generic parameter, roughly `where -1: 11` + * | | | since -1 is the type parameter and 11 the trait + * | | - function output 5<-1> + * | - the overall function signature is something like + * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` + * - function input, corresponds roughly to 4<-1> + * 4 is an index into the `p` array for a type + * -1 is the generic parameter, given by 11 + * + * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like + * function inputs and outputs: + * + * [-1, -1, [4, 3]] + * ^^^^^^ where -1: 4 + 3 + * + * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array + * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in + * favor of `4 + 3`: + * + * [-1, -1, [[4, 3]]] + * ^^^^^^^^ where -1: 4 + 3 + * + * [-1, -1, [5, [4, 3]]] + * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 + * + * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i + * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. + * * @typedef {( * 0 | * [(number|Array)] | - * [(number|Array), (number|Array)] + * [(number|Array), (number|Array)] | + * Array<(number|Array)> * )} */ let RawFunctionSearchType; @@ -136,6 +183,7 @@ let RawFunctionType; * @typedef {{ * inputs: Array, * output: Array, + * where_clause: Array>, * }} */ let FunctionSearchType; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 254b0d8bf..eb256455b 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -49,10 +49,12 @@ window.currentCrate = getVar("current-crate"); function setMobileTopbar() { // FIXME: It would be nicer to generate this text content directly in HTML, // but with the current code it's hard to get the right information in the right place. - const mobileLocationTitle = document.querySelector(".mobile-topbar h2"); + const mobileTopbar = document.querySelector(".mobile-topbar"); const locationTitle = document.querySelector(".sidebar h2.location"); - if (mobileLocationTitle && locationTitle) { - mobileLocationTitle.innerHTML = locationTitle.innerHTML; + if (mobileTopbar && locationTitle) { + const mobileTitle = document.createElement("h2"); + mobileTitle.innerHTML = locationTitle.innerHTML; + mobileTopbar.appendChild(mobileTitle); } } @@ -176,13 +178,6 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } -function loadCss(cssUrl) { - const link = document.createElement("link"); - link.href = cssUrl; - link.rel = "stylesheet"; - document.getElementsByTagName("head")[0].appendChild(link); -} - function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -210,11 +205,7 @@ function preLoadCss(cssUrl) { event.preventDefault(); // Sending request for the CSS and the JS files at the same time so it will // hopefully be loaded when the JS will generate the settings content. - loadCss(getVar("static-root-path") + getVar("settings-css")); loadScript(getVar("static-root-path") + getVar("settings-js")); - preLoadCss(getVar("static-root-path") + getVar("theme-light-css")); - preLoadCss(getVar("static-root-path") + getVar("theme-dark-css")); - preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css")); // Pre-load all theme CSS files, so that switching feels seamless. // // When loading settings.html as a standalone page, the equivalent HTML is @@ -499,7 +490,7 @@ function preLoadCss(cssUrl) { block("static", "static", "Statics"); block("trait", "traits", "Traits"); block("fn", "functions", "Functions"); - block("type", "types", "Type Definitions"); + block("type", "types", "Type Aliases"); block("foreigntype", "foreign-types", "Foreign Types"); block("keyword", "keywords", "Keywords"); block("traitalias", "trait-aliases", "Trait Aliases"); @@ -852,14 +843,14 @@ function preLoadCss(cssUrl) { window.CURRENT_TOOLTIP_ELEMENT = wrapper; window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); - wrapper.onpointerenter = function(ev) { + wrapper.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } clearTooltipHoverTimeout(e); }; - wrapper.onpointerleave = function(ev) { + wrapper.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; @@ -963,38 +954,38 @@ function preLoadCss(cssUrl) { } onEachLazy(document.getElementsByClassName("tooltip"), e => { - e.onclick = function() { - this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true; - if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) { + e.onclick = () => { + e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; + if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) { hideTooltip(true); } else { - showTooltip(this); + showTooltip(e); window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); window.CURRENT_TOOLTIP_ELEMENT.focus(); window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; - e.onpointerenter = function(ev) { + e.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - setTooltipHoverTimeout(this, true); + setTooltipHoverTimeout(e, true); }; - e.onpointermove = function(ev) { + e.onpointermove = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - setTooltipHoverTimeout(this, true); + setTooltipHoverTimeout(e, true); }; - e.onpointerleave = function(ev) { + e.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - if (!this.TOOLTIP_FORCE_VISIBLE && + if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) { // Tooltip pointer leave gesture: // @@ -1139,7 +1130,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\ * * Pass "true" to reset focus for tooltip popovers. */ - window.hideAllModals = function(switchFocus) { + window.hideAllModals = switchFocus => { hideSidebar(); window.hidePopoverMenus(); hideTooltip(switchFocus); @@ -1148,7 +1139,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\ /** * Hide all the popover menus. */ - window.hidePopoverMenus = function() { + window.hidePopoverMenus = () => { onEachLazy(document.querySelectorAll(".search-form .popover"), elem => { elem.style.display = "none"; }); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 42088e735..2f0cae0a4 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3,6 +3,17 @@ "use strict"; +// polyfill +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced +if (!Array.prototype.toSpliced) { + // Can't use arrow functions, because we want `this` + Array.prototype.toSpliced = function() { + const me = this.slice(); + Array.prototype.splice.apply(me, arguments); + return me; + }; +} + (function() { // This mapping table should match the discriminants of // `rustdoc::formats::item_type::ItemType` type in Rust. @@ -33,6 +44,7 @@ const itemTypes = [ "attr", "derive", "traitalias", + "generic", ]; const longItemTypes = [ @@ -67,6 +79,7 @@ const longItemTypes = [ // used for special search precedence const TY_PRIMITIVE = itemTypes.indexOf("primitive"); const TY_KEYWORD = itemTypes.indexOf("keyword"); +const TY_GENERIC = itemTypes.indexOf("generic"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; function hasOwnPropertyRustdoc(obj, property) { @@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) { /** * Add an item to the type Name->ID map, or, if one already exists, use it. - * Returns the number. If name is "" or null, return -1 (pure generic). + * Returns the number. If name is "" or null, return null (pure generic). * * This is effectively string interning, so that function matching can be * done more quickly. Two types with the same name but different item kinds @@ -263,9 +276,8 @@ function initSearch(rawSearchIndex) { * @returns {integer} */ function buildTypeMapIndex(name) { - if (name === "" || name === null) { - return -1; + return null; } if (typeNameIdMap.has(name)) { @@ -490,7 +502,7 @@ function initSearch(rawSearchIndex) { } return { name: "never", - id: -1, + id: null, fullPath: ["never"], pathWithoutLast: [], pathLast: "never", @@ -532,7 +544,7 @@ function initSearch(rawSearchIndex) { } return { name: name.trim(), - id: -1, + id: null, fullPath: pathSegments, pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), pathLast: pathSegments[pathSegments.length - 1], @@ -661,7 +673,7 @@ function initSearch(rawSearchIndex) { } elems.push({ name: "[]", - id: -1, + id: null, fullPath: ["[]"], pathWithoutLast: [], pathLast: "[]", @@ -972,9 +984,13 @@ function initSearch(rawSearchIndex) { returned: [], // Total number of "top" elements (does not include generics). foundElems: 0, + // Total number of elements (includes generics). + totalElems: 0, literalSearch: false, error: null, correction: null, + proposeCorrectionFrom: null, + proposeCorrectionTo: null, }; } @@ -1015,64 +1031,10 @@ function initSearch(rawSearchIndex) { /** * Parses the query. * - * The supported syntax by this parser is as follow: - * - * ident = *(ALPHA / DIGIT / "_") - * path = ident *(DOUBLE-COLON/{WS} ident) [!] - * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET - * arg = [type-filter *WS COLON *WS] (path [generics] / slice) - * type-sep = *WS COMMA *(COMMA) - * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) - * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep) - * CLOSE-ANGLE-BRACKET - * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list - * - * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] - * type-search = [ nonempty-arg-list ] [ return-args ] - * - * query = *WS (exact-search / type-search) *WS + * The supported syntax by this parser is given in the rustdoc book chapter + * /src/doc/rustdoc/src/read-documentation/search.md * - * type-filter = ( - * "mod" / - * "externcrate" / - * "import" / - * "struct" / - * "enum" / - * "fn" / - * "type" / - * "static" / - * "trait" / - * "impl" / - * "tymethod" / - * "method" / - * "structfield" / - * "variant" / - * "macro" / - * "primitive" / - * "associatedtype" / - * "constant" / - * "associatedconstant" / - * "union" / - * "foreigntype" / - * "keyword" / - * "existential" / - * "attr" / - * "derive" / - * "traitalias") - * - * OPEN-ANGLE-BRACKET = "<" - * CLOSE-ANGLE-BRACKET = ">" - * OPEN-SQUARE-BRACKET = "[" - * CLOSE-SQUARE-BRACKET = "]" - * COLON = ":" - * DOUBLE-COLON = "::" - * QUOTE = %x22 - * COMMA = "," - * RETURN-ARROW = "->" - * - * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z - * DIGIT = %x30-39 - * WS = %x09 / " " + * When adding new things to the parser, add them there, too! * * @param {string} val - The user query * @@ -1125,6 +1087,7 @@ function initSearch(rawSearchIndex) { query.literalSearch = parserState.totalElems > 1; } query.foundElems = query.elems.length + query.returned.length; + query.totalElems = parserState.totalElems; return query; } @@ -1173,7 +1136,7 @@ function initSearch(rawSearchIndex) { const out = []; for (const result of results) { - if (result.id > -1) { + if (result.id !== -1) { const obj = searchIndex[result.id]; obj.dist = result.dist; const res = buildHrefAndPath(obj); @@ -1349,166 +1312,311 @@ function initSearch(rawSearchIndex) { * This function checks generics in search query `queryElem` can all be found in the * search index (`fnType`), * - * @param {FunctionType} fnType - The object to check. - * @param {QueryElement} queryElem - The element from the parsed query. + * This function returns `true` if it matches, and also writes the results to mgensInout. + * It returns `false` if no match is found, and leaves mgensInout untouched. + * + * @param {FunctionType} fnType - The object to check. + * @param {QueryElement} queryElem - The element from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensInout - Map functions generics to query generics. * * @return {boolean} - Returns true if a match, false otherwise. */ - function checkGenerics(fnType, queryElem) { - return unifyFunctionTypes(fnType.generics, queryElem.generics); + function checkGenerics(fnType, queryElem, whereClause, mgensInout) { + return unifyFunctionTypes( + fnType.generics, + queryElem.generics, + whereClause, + mgensInout, + mgens => { + if (mgensInout) { + for (const [fid, qid] of mgens.entries()) { + mgensInout.set(fid, qid); + } + } + return true; + } + ); } /** * This function checks if a list of search query `queryElems` can all be found in the * search index (`fnTypes`). * - * @param {Array} fnTypes - The objects to check. + * This function returns `true` on a match, or `false` if none. If `solutionCb` is + * supplied, it will call that function with mgens, and that callback can accept or + * reject the result bu returning `true` or `false`. If the callback returns false, + * then this function will try with a different solution, or bail with false if it + * runs out of candidates. + * + * @param {Array} fnTypes - The objects to check. * @param {Array} queryElems - The elements from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn + * - Map functions generics to query generics (never modified). + * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. * * @return {boolean} - Returns true if a match, false otherwise. */ - function unifyFunctionTypes(fnTypes, queryElems) { - // This search engine implements order-agnostic unification. There - // should be no missing duplicates (generics have "bag semantics"), - // and the row is allowed to have extras. + function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) { + /** + * @type Map + */ + let mgens = new Map(mgensIn); if (queryElems.length === 0) { - return true; + return !solutionCb || solutionCb(mgens); } - if (!fnTypes || fnTypes.length === 0) { + if (!fnTypesIn || fnTypesIn.length === 0) { return false; } + const ql = queryElems.length; + let fl = fnTypesIn.length; /** - * @type Map + * @type Array */ - const queryElemSet = new Map(); - const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet(queryElem) { - let currentQueryElemList; - if (queryElemSet.has(queryElem.id)) { - currentQueryElemList = queryElemSet.get(queryElem.id); - } else { - currentQueryElemList = []; - queryElemSet.set(queryElem.id, currentQueryElemList); - } - currentQueryElemList.push(queryElem); - }; - for (const queryElem of queryElems) { - addQueryElemToQueryElemSet(queryElem); - } + let fnTypes = fnTypesIn.slice(); /** - * @type Map + * loop works by building up a solution set in the working arrays + * fnTypes gets mutated in place to make this work, while queryElems + * is left alone + * + * vvvvvvv `i` points here + * queryElems = [ good, good, good, unknown, unknown ], + * fnTypes = [ good, good, good, unknown, unknown ], + * ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`, + * | looking for candidates + * everything before `i` is the + * current working solution + * + * Everything in the current working solution is known to be a good + * match, but it might not be the match we wind up going with, because + * there might be more than one candidate match, and we need to try them all + * before giving up. So, to handle this, it backtracks on failure. + * + * @type Array<{ + * "fnTypesScratch": Array, + * "queryElemsOffset": integer, + * "fnTypesOffset": integer + * }> */ - const fnTypeSet = new Map(); - const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet(fnType) { - // Pure generic, or an item that's not matched by any query elems. - // Try [unboxing] it. - // - // [unboxing]: - // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf - const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice); - if (fnType.id === -1 || !( - queryElemSet.has(fnType.id) || - (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) || - (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem) - )) { - for (const innerFnType of fnType.generics) { - addFnTypeToFnTypeSet(innerFnType); - } - return; - } - let currentQueryElemList = queryElemSet.get(fnType.id) || []; - let matchIdx = currentQueryElemList.findIndex(queryElem => { - return typePassesFilter(queryElem.typeFilter, fnType.ty) && - checkGenerics(fnType, queryElem); - }); - if (matchIdx === -1 && - (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) && - queryContainsArrayOrSliceElem - ) { - currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || []; - matchIdx = currentQueryElemList.findIndex(queryElem => { - return typePassesFilter(queryElem.typeFilter, fnType.ty) && - checkGenerics(fnType, queryElem); - }); - } - // None of the query elems match the function type. - // Try [unboxing] it. - if (matchIdx === -1) { - for (const innerFnType of fnType.generics) { - addFnTypeToFnTypeSet(innerFnType); + const backtracking = []; + let i = 0; + let j = 0; + const backtrack = () => { + while (backtracking.length !== 0) { + // this session failed, but there are other possible solutions + // to backtrack, reset to (a copy of) the old array, do the swap or unboxing + const { + fnTypesScratch, + mgensScratch, + queryElemsOffset, + fnTypesOffset, + unbox, + } = backtracking.pop(); + mgens = new Map(mgensScratch); + const fnType = fnTypesScratch[fnTypesOffset]; + const queryElem = queryElems[queryElemsOffset]; + if (unbox) { + if (fnType.id < 0) { + if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { + continue; + } + mgens.set(fnType.id, 0); + } + const generics = fnType.id < 0 ? + whereClause[(-fnType.id) - 1] : + fnType.generics; + fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics); + fl = fnTypes.length; + // re-run the matching algorithm on this item + i = queryElemsOffset - 1; + } else { + if (fnType.id < 0) { + if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) { + continue; + } + mgens.set(fnType.id, queryElem.id); + } + fnTypes = fnTypesScratch.slice(); + fl = fnTypes.length; + const tmp = fnTypes[queryElemsOffset]; + fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset]; + fnTypes[fnTypesOffset] = tmp; + // this is known as a good match; go to the next one + i = queryElemsOffset; } - return; - } - let currentFnTypeList; - if (fnTypeSet.has(fnType.id)) { - currentFnTypeList = fnTypeSet.get(fnType.id); - } else { - currentFnTypeList = []; - fnTypeSet.set(fnType.id, currentFnTypeList); + return true; } - currentFnTypeList.push(fnType); + return false; }; - for (const fnType of fnTypes) { - addFnTypeToFnTypeSet(fnType); - } - const doHandleQueryElemList = (currentFnTypeList, queryElemList) => { - if (queryElemList.length === 0) { - return true; + for (i = 0; i !== ql; ++i) { + const queryElem = queryElems[i]; + /** + * list of potential function types that go with the current query element. + * @type Array + */ + const matchCandidates = []; + let fnTypesScratch = null; + let mgensScratch = null; + // don't try anything before `i`, because they've already been + // paired off with the other query elements + for (j = i; j !== fl; ++j) { + const fnType = fnTypes[j]; + if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + if (!fnTypesScratch) { + fnTypesScratch = fnTypes.slice(); + } + unifyFunctionTypes( + fnType.generics, + queryElem.generics, + whereClause, + mgens, + mgensScratch => { + matchCandidates.push({ + fnTypesScratch, + mgensScratch, + queryElemsOffset: i, + fnTypesOffset: j, + unbox: false, + }); + return false; // "reject" all candidates to gather all of them + } + ); + } + if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + if (!fnTypesScratch) { + fnTypesScratch = fnTypes.slice(); + } + if (!mgensScratch) { + mgensScratch = new Map(mgens); + } + backtracking.push({ + fnTypesScratch, + mgensScratch, + queryElemsOffset: i, + fnTypesOffset: j, + unbox: true, + }); + } } - // Multiple items in one list might match multiple items in another. - // Since an item with fewer generics can match an item with more, we - // need to check all combinations for a potential match. - const queryElem = queryElemList.pop(); - const l = currentFnTypeList.length; - for (let i = 0; i < l; i += 1) { - const fnType = currentFnTypeList[i]; - if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { + if (matchCandidates.length === 0) { + if (backtrack()) { continue; + } else { + return false; } - if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) { - currentFnTypeList.splice(i, 1); - const result = doHandleQueryElemList(currentFnTypeList, queryElemList); - if (result) { - return true; - } - currentFnTypeList.splice(i, 0, fnType); + } + // use the current candidate + const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop(); + if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) { + mgens.set(fnTypes[candidate].id, queryElems[i].id); + } + for (const [fid, qid] of mgensNew) { + mgens.set(fid, qid); + } + // `i` and `j` are paired off + // `queryElems[i]` is left in place + // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off + const tmp = fnTypes[candidate]; + fnTypes[candidate] = fnTypes[i]; + fnTypes[i] = tmp; + // write other candidates to backtracking queue + for (const otherCandidate of matchCandidates) { + backtracking.push(otherCandidate); + } + // If we're on the last item, check the solution with the callback + // backtrack if the callback says its unsuitable + while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) { + if (!backtrack()) { + return false; } } + } + return true; + } + function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) { + // type filters look like `trait:Read` or `enum:Result` + if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; - }; - const handleQueryElemList = (id, queryElemList) => { - if (!fnTypeSet.has(id)) { - if (id === typeNameIdOfArrayOrSlice) { - return handleQueryElemList(typeNameIdOfSlice, queryElemList) || - handleQueryElemList(typeNameIdOfArray, queryElemList); + } + // fnType.id < 0 means generic + // queryElem.id < 0 does too + // mgens[fnType.id] = queryElem.id + // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait + // and should make that same decision everywhere it appears + if (fnType.id < 0 && queryElem.id < 0) { + if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) { + return false; + } + for (const [fid, qid] of mgens.entries()) { + if (fnType.id !== fid && queryElem.id === qid) { + return false; + } + if (fnType.id === fid && queryElem.id !== qid) { + return false; } + } + } else if (fnType.id !== null) { + if (queryElem.id === typeNameIdOfArrayOrSlice && + (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) + ) { + // [] matches primitive:array or primitive:slice + // if it matches, then we're fine, and this is an appropriate match candidate + } else if (fnType.id !== queryElem.id) { return false; } - const currentFnTypeList = fnTypeSet.get(id); - if (currentFnTypeList.length < queryElemList.length) { - // It's not possible for all the query elems to find a match. + // If the query elem has generics, and the function doesn't, + // it can't match. + if (fnType.generics.length === 0 && queryElem.generics.length !== 0) { return false; } - const result = doHandleQueryElemList(currentFnTypeList, queryElemList); - if (result) { - // Found a solution. - // Any items that weren't used for it can be unboxed, and might form - // part of the solution for another item. - for (const innerFnType of currentFnTypeList) { - addFnTypeToFnTypeSet(innerFnType); + // If the query element is a path (it contains `::`), we need to check if this + // path is compatible with the target type. + const queryElemPathLength = queryElem.pathWithoutLast.length; + if (queryElemPathLength > 0) { + const fnTypePath = fnType.path !== undefined && fnType.path !== null ? + fnType.path.split("::") : []; + // If the path provided in the query element is longer than this type, + // no need to check it since it won't match in any case. + if (queryElemPathLength > fnTypePath.length) { + return false; } - fnTypeSet.delete(id); - } - return result; - }; - let queryElemSetSize = -1; - while (queryElemSetSize !== queryElemSet.size) { - queryElemSetSize = queryElemSet.size; - for (const [id, queryElemList] of queryElemSet) { - if (handleQueryElemList(id, queryElemList)) { - queryElemSet.delete(id); + let i = 0; + for (const path of fnTypePath) { + if (path === queryElem.pathWithoutLast[i]) { + i += 1; + if (i >= queryElemPathLength) { + break; + } + } + } + if (i < queryElemPathLength) { + // If we didn't find all parts of the path of the query element inside + // the fn type, then it's not the right one. + return false; } } } - return queryElemSetSize === 0; + return true; + } + function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) { + if (fnType.id < 0 && queryElem.id >= 0) { + if (!whereClause) { + return false; + } + // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic + // mgens[fnType.id] === null indicates that we haven't decided yet + if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { + return false; + } + // This is only a potential unbox if the search query appears in the where clause + // for example, searching `Read -> usize` should find + // `fn read_all(R) -> Result` + // generic `R` is considered "unboxed" + return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause); + } else if (fnType.generics && fnType.generics.length > 0) { + return checkIfInList(fnType.generics, queryElem, whereClause); + } + return false; } /** @@ -1516,13 +1624,14 @@ function initSearch(rawSearchIndex) { * generics (if any). * * @param {Array} list - * @param {QueryElement} elem - The element from the parsed query. + * @param {QueryElement} elem - The element from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. * * @return {boolean} - Returns true if found, false otherwise. */ - function checkIfInList(list, elem) { + function checkIfInList(list, elem, whereClause) { for (const entry of list) { - if (checkType(entry, elem)) { + if (checkType(entry, elem, whereClause)) { return true; } } @@ -1534,14 +1643,26 @@ function initSearch(rawSearchIndex) { * generics (if any). * * @param {Row} row - * @param {QueryElement} elem - The element from the parsed query. + * @param {QueryElement} elem - The element from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. * * @return {boolean} - Returns true if the type matches, false otherwise. */ - function checkType(row, elem) { - if (row.id === -1) { + function checkType(row, elem, whereClause) { + if (row.id === null) { // This is a pure "generic" search, no need to run other checks. - return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false; + return row.generics.length > 0 + ? checkIfInList(row.generics, elem, whereClause) + : false; + } + + if (row.id < 0 && elem.id >= 0) { + const gid = (-row.id) - 1; + return checkIfInList(whereClause[gid], elem, whereClause); + } + + if (row.id < 0 && elem.id < 0) { + return true; } const matchesExact = row.id === elem.id; @@ -1551,7 +1672,7 @@ function initSearch(rawSearchIndex) { if ((matchesExact || matchesArrayOrSlice) && typePassesFilter(elem.typeFilter, row.ty)) { if (elem.generics.length > 0) { - return checkGenerics(row, elem); + return checkGenerics(row, elem, whereClause, new Map()); } return true; } @@ -1559,7 +1680,7 @@ function initSearch(rawSearchIndex) { // If the current item does not match, try [unboxing] the generic. // [unboxing]: // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf - return checkIfInList(row.generics, elem); + return checkIfInList(row.generics, elem, whereClause); } function checkPath(contains, ty, maxEditDistance) { @@ -1760,13 +1881,15 @@ function initSearch(rawSearchIndex) { const fullId = row.id; const searchWord = searchWords[pos]; - const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem); + const in_args = row.type && row.type.inputs + && checkIfInList(row.type.inputs, elem, row.type.where_clause); if (in_args) { // path_dist is 0 because no parent path information is currently stored // in the search index addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance); } - const returned = row.type && row.type.output && checkIfInList(row.type.output, elem); + const returned = row.type && row.type.output + && checkIfInList(row.type.output, elem, row.type.where_clause); if (returned) { addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance); } @@ -1828,10 +1951,20 @@ function initSearch(rawSearchIndex) { } // If the result is too "bad", we return false and it ends this search. - if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) { - return; - } - if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) { + if (!unifyFunctionTypes( + row.type.inputs, + parsedQuery.elems, + row.type.where_clause, + null, + mgens => { + return unifyFunctionTypes( + row.type.output, + parsedQuery.returned, + row.type.where_clause, + mgens + ); + } + )) { return; } @@ -1850,6 +1983,11 @@ function initSearch(rawSearchIndex) { } const maxEditDistance = Math.floor(queryLen / 3); + /** + * @type {Map} + */ + const genericSymbols = new Map(); + /** * Convert names to ids in parsed query elements. * This is not used for the "In Names" tab, but is used for the @@ -1863,14 +2001,14 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem */ function convertNameToId(elem) { - if (typeNameIdMap.has(elem.name)) { - elem.id = typeNameIdMap.get(elem.name); + if (typeNameIdMap.has(elem.pathLast)) { + elem.id = typeNameIdMap.get(elem.pathLast); } else if (!parsedQuery.literalSearch) { - let match = -1; + let match = null; let matchDist = maxEditDistance + 1; let matchName = ""; for (const [name, id] of typeNameIdMap) { - const dist = editDistance(name, elem.name, maxEditDistance); + const dist = editDistance(name, elem.pathLast, maxEditDistance); if (dist <= matchDist && dist <= maxEditDistance) { if (dist === matchDist && matchName > name) { continue; @@ -1880,11 +2018,52 @@ function initSearch(rawSearchIndex) { matchName = name; } } - if (match !== -1) { + if (match !== null) { parsedQuery.correction = matchName; } elem.id = match; } + if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 + && elem.generics.length === 0) + || elem.typeFilter === TY_GENERIC) { + if (genericSymbols.has(elem.name)) { + elem.id = genericSymbols.get(elem.name); + } else { + elem.id = -(genericSymbols.size + 1); + genericSymbols.set(elem.name, elem.id); + } + if (elem.typeFilter === -1 && elem.name.length >= 3) { + // Silly heuristic to catch if the user probably meant + // to not write a generic parameter. We don't use it, + // just bring it up. + const maxPartDistance = Math.floor(elem.name.length / 3); + let matchDist = maxPartDistance + 1; + let matchName = ""; + for (const name of typeNameIdMap.keys()) { + const dist = editDistance(name, elem.name, maxPartDistance); + if (dist <= matchDist && dist <= maxPartDistance) { + if (dist === matchDist && matchName > name) { + continue; + } + matchDist = dist; + matchName = name; + } + } + if (matchName !== "") { + parsedQuery.proposeCorrectionFrom = elem.name; + parsedQuery.proposeCorrectionTo = matchName; + } + } + elem.typeFilter = TY_GENERIC; + } + if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { + // Rust does not have HKT + parsedQuery.error = [ + "Generic type parameter ", + elem.name, + " does not accept generic parameters", + ]; + } for (const elem2 of elem.generics) { convertNameToId(elem2); } @@ -1918,8 +2097,11 @@ function initSearch(rawSearchIndex) { elem = parsedQuery.returned[0]; for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { row = searchIndex[i]; - in_returned = row.type && - unifyFunctionTypes(row.type.output, parsedQuery.returned); + in_returned = row.type && unifyFunctionTypes( + row.type.output, + parsedQuery.returned, + row.type.where_clause + ); if (in_returned) { addIntoResults( results_others, @@ -2152,11 +2334,20 @@ ${item.displayPath}${name}\ } function makeTabHeader(tabNb, text, nbElems) { + // https://blog.horizon-eda.org/misc/2020/02/19/ui.html + // + // CSS runs with `font-variant-numeric: tabular-nums` to ensure all + // digits are the same width. \u{2007} is a Unicode space character + // that is defined to be the same width as a digit. + const fmtNbElems = + nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : + nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : + `\u{2007}(${nbElems})`; if (searchState.currentTab === tabNb) { return ""; + "" + fmtNbElems + ""; } - return ""; + return ""; } /** @@ -2270,6 +2461,13 @@ ${item.displayPath}${name}\ "Showing results for closest type name " + `"${results.query.correction}" instead.`; } + if (results.query.proposeCorrectionFrom !== null) { + const orig = results.query.proposeCorrectionFrom; + const targ = results.query.proposeCorrectionTo; + output += "

" + + `Type "${orig}" not found and used as generic parameter. ` + + `Consider searching for "${targ}" instead.

`; + } const resultsElem = document.createElement("div"); resultsElem.id = "results"; @@ -2371,29 +2569,54 @@ ${item.displayPath}${name}\ * @return {Array} */ function buildItemSearchTypeAll(types, lowercasePaths) { + return types.map(type => buildItemSearchType(type, lowercasePaths)); + } + + /** + * Converts a single type. + * + * @param {RawFunctionType} type + */ + function buildItemSearchType(type, 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 - ); - } + let pathIndex, generics; + if (typeof type === "number") { + pathIndex = type; + generics = []; + } else { + pathIndex = type[PATH_INDEX_DATA]; + generics = buildItemSearchTypeAll( + type[GENERICS_DATA], + lowercasePaths + ); + } + if (pathIndex < 0) { + // types less than 0 are generic parameters + // the actual names of generic parameters aren't stored, since they aren't API return { - // `0` is used as a sentinel because it's fewer bytes than `null` - id: pathIndex === 0 - ? -1 - : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name), - ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, - generics: generics, + id: pathIndex, + ty: TY_GENERIC, + path: null, + generics, }; - }); + } + if (pathIndex === 0) { + // `0` is used as a sentinel because it's fewer bytes than `null` + return { + id: null, + ty: null, + path: null, + generics, + }; + } + const item = lowercasePaths[pathIndex - 1]; + return { + id: buildTypeMapIndex(item.name), + ty: item.ty, + path: item.path, + generics, + }; } /** @@ -2421,14 +2644,7 @@ ${item.displayPath}${name}\ } let inputs, output; if (typeof functionSearchType[INPUTS_DATA] === "number") { - const pathIndex = functionSearchType[INPUTS_DATA]; - inputs = [{ - id: pathIndex === 0 - ? -1 - : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name), - ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, - generics: [], - }]; + inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; } else { inputs = buildItemSearchTypeAll( functionSearchType[INPUTS_DATA], @@ -2437,14 +2653,7 @@ ${item.displayPath}${name}\ } if (functionSearchType.length > 1) { if (typeof functionSearchType[OUTPUT_DATA] === "number") { - const pathIndex = functionSearchType[OUTPUT_DATA]; - output = [{ - id: pathIndex === 0 - ? -1 - : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name), - ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, - generics: [], - }]; + output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; } else { output = buildItemSearchTypeAll( functionSearchType[OUTPUT_DATA], @@ -2454,8 +2663,15 @@ ${item.displayPath}${name}\ } else { output = []; } + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] + : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + } return { - inputs, output, + inputs, output, where_clause, }; } @@ -2486,18 +2702,25 @@ ${item.displayPath}${name}\ let 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 string of characters that represent an - * offset in `itemTypes`). d[i] contains the description of that item. + * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` + * are arrays with the same length. `q`, `a`, and `c` use a sparse + * representation for compactness. + * + * `n[i]` contains the name of an item. + * + * `t[i]` contains the type of that item + * (as a string of characters that represent an offset in `itemTypes`). * - * q[i] contains the full path of the item, or an empty string indicating - * "same as q[i-1]". + * `d[i]` contains the description of that item. * - * i[i] contains an item's parent, usually a module. For compactness, + * `q` contains the full paths of the items. For compactness, it is a set of + * (index, path) pairs used to create a map. If a given index `i` is + * not present, this indicates "same as the last index present". + * + * `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. + * `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. @@ -2511,6 +2734,8 @@ ${item.displayPath}${name}\ * * `p` is a list of path/type pairs. It is used for parents and function parameters. * + * `c` is an array of item indices that are deprecated. + * * @type {{ * doc: string, * a: Object, @@ -2577,9 +2802,19 @@ ${item.displayPath}${name}\ // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; + let lastPath = itemPaths.get(0); for (let 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]}; + const elem = paths[i]; + const ty = elem[0]; + const name = elem[1]; + let path = null; + if (elem.length > 2) { + path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; + lastPath = path; + } + + lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path}); + paths[i] = {ty: ty, name: name, path: path}; } // convert `item*` into an object form, and construct word indices. @@ -2589,8 +2824,8 @@ ${item.displayPath}${name}\ // operation that is cached for the life of the page state so that // all other search operations have access to this cached data for // faster analysis operations + lastPath = ""; len = itemTypes.length; - let lastPath = ""; for (let i = 0; i < len; ++i) { let word = ""; // This object should have exactly the same set of fields as the "crateRow" @@ -2599,11 +2834,12 @@ ${item.displayPath}${name}\ word = itemNames[i].toLowerCase(); } searchWords.push(word); + const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; const row = { crate: crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: itemPaths.has(i) ? itemPaths.get(i) : lastPath, + path: path, desc: itemDescs[i], parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type: buildFunctionSearchType( diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 2cba32c1b..63947789c 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -59,8 +59,8 @@ if (settingValue !== null) { toggle.checked = settingValue === "true"; } - toggle.onchange = function() { - changeSetting(this.id, this.checked); + toggle.onchange = () => { + changeSetting(toggle.id, toggle.checked); }; }); onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { @@ -224,14 +224,14 @@ if (isSettingsPage) { // We replace the existing "onclick" callback to do nothing if clicked. - getSettingsButton().onclick = function(event) { + getSettingsButton().onclick = event => { event.preventDefault(); }; } else { // We replace the existing "onclick" callback. const settingsButton = getSettingsButton(); const settingsMenu = document.getElementById("settings"); - settingsButton.onclick = function(event) { + settingsButton.onclick = event => { if (elemIsInParent(event.target, settingsMenu)) { return; } diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index af3ca42a6..c69641092 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -5,6 +5,7 @@ // the page, so we don't see major layout changes during the load of the page. "use strict"; +const builtinThemes = ["light", "dark", "ayu"]; const darkThemes = ["dark", "ayu"]; window.currentTheme = document.getElementById("themeStyle"); @@ -119,19 +120,32 @@ function switchTheme(newThemeName, saveTheme) { updateLocalStorage("theme", newThemeName); } - let newHref; + document.documentElement.setAttribute("data-theme", newThemeName); - if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") { - newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css"); + if (builtinThemes.indexOf(newThemeName) !== -1) { + if (window.currentTheme) { + window.currentTheme.parentNode.removeChild(window.currentTheme); + window.currentTheme = null; + } } else { - newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css"; - } - - if (!window.currentTheme) { - document.write(``); - window.currentTheme = document.getElementById("themeStyle"); - } else if (newHref !== window.currentTheme.href) { - window.currentTheme.href = newHref; + const newHref = getVar("root-path") + newThemeName + + getVar("resource-suffix") + ".css"; + if (!window.currentTheme) { + // If we're in the middle of loading, document.write blocks + // rendering, but if we are done, it would blank the page. + if (document.readyState === "loading") { + document.write(``); + window.currentTheme = document.getElementById("themeStyle"); + } else { + window.currentTheme = document.createElement("link"); + window.currentTheme.rel = "stylesheet"; + window.currentTheme.id = "themeStyle"; + window.currentTheme.href = newHref; + document.documentElement.appendChild(window.currentTheme); + } + } else if (newHref !== window.currentTheme.href) { + window.currentTheme.href = newHref; + } } } -- cgit v1.2.3