diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/librustdoc/html/static | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/html/static')
42 files changed, 8998 insertions, 0 deletions
diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js new file mode 100644 index 000000000..fcd925bb3 --- /dev/null +++ b/src/librustdoc/html/static/.eslintrc.js @@ -0,0 +1,96 @@ +module.exports = { + "env": { + "browser": true, + "es6": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2015, + "sourceType": "module" + }, + "rules": { + "linebreak-style": [ + "error", + "unix" + ], + "semi": [ + "error", + "always" + ], + "quotes": [ + "error", + "double" + ], + "linebreak-style": [ + "error", + "unix" + ], + "no-trailing-spaces": "error", + "no-var": ["error"], + "prefer-const": ["error"], + "prefer-arrow-callback": ["error"], + "brace-style": [ + "error", + "1tbs", + { "allowSingleLine": false } + ], + "keyword-spacing": [ + "error", + { "before": true, "after": true } + ], + "arrow-spacing": [ + "error", + { "before": true, "after": true } + ], + "key-spacing": [ + "error", + { "beforeColon": false, "afterColon": true, "mode": "strict" } + ], + "func-call-spacing": ["error", "never"], + "space-infix-ops": "error", + "space-before-function-paren": ["error", "never"], + "space-before-blocks": "error", + "comma-dangle": ["error", "always-multiline"], + "comma-style": ["error", "last"], + "max-len": ["error", { "code": 100, "tabWidth": 4 }], + "eol-last": ["error", "always"], + "arrow-parens": ["error", "as-needed"], + "no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ], + "eqeqeq": "error", + "no-const-assign": "error", + "no-debugger": "error", + "no-dupe-args": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-ex-assign": "error", + "no-fallthrough": "error", + "no-invalid-regexp": "error", + "no-import-assign": "error", + "no-self-compare": "error", + "no-template-curly-in-string": "error", + "block-scoped-var": "error", + "guard-for-in": "error", + "no-alert": "error", + "no-confusing-arrow": "error", + "no-div-regex": "error", + "no-floating-decimal": "error", + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-label-var": "error", + "no-lonely-if": "error", + "no-mixed-operators": "error", + "no-multi-assign": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-sequences": "error", + "no-throw-literal": "error", + "no-div-regex": "error", + } +}; diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt new file mode 100644 index 000000000..34e48134c --- /dev/null +++ b/src/librustdoc/html/static/COPYRIGHT.txt @@ -0,0 +1,46 @@ +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. diff --git a/src/librustdoc/html/static/LICENSE-APACHE.txt b/src/librustdoc/html/static/LICENSE-APACHE.txt new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/src/librustdoc/html/static/LICENSE-APACHE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/librustdoc/html/static/LICENSE-MIT.txt b/src/librustdoc/html/static/LICENSE-MIT.txt new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/src/librustdoc/html/static/LICENSE-MIT.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/librustdoc/html/static/css/normalize.css b/src/librustdoc/html/static/css/normalize.css new file mode 100644 index 000000000..fdb8a8c65 --- /dev/null +++ b/src/librustdoc/html/static/css/normalize.css @@ -0,0 +1,2 @@ +/* ignore-tidy-linelength */ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css new file mode 100644 index 000000000..0a19a99ab --- /dev/null +++ b/src/librustdoc/html/static/css/noscript.css @@ -0,0 +1,20 @@ +/* +This whole CSS file is used only in case rustdoc is rendered with javascript disabled. Since a lot +of content is hidden by default (depending on the settings too), we have to overwrite some of the +rules. +*/ + +#main-content .attributes { + /* Since there is no toggle (the "[-]") when JS is disabled, no need for this margin either. */ + margin-left: 0 !important; +} + +#copy-path { + /* It requires JS to work so no need to display it in this case. */ + display: none; +} + +.sub { + /* The search bar and related controls don't work without JS */ + display: none; +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css new file mode 100644 index 000000000..83fe14550 --- /dev/null +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -0,0 +1,2335 @@ +/* See FiraSans-LICENSE.txt for the Fira Sans license. */ +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 400; + src: local('Fira Sans'), + url("FiraSans-Regular.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 500; + src: local('Fira Sans Medium'), + url("FiraSans-Medium.woff2") format("woff2"); + font-display: swap; +} + +/* See SourceSerif4-LICENSE.md for the Source Serif 4 license. */ +@font-face { + font-family: 'Source Serif 4'; + font-style: normal; + font-weight: 400; + src: local('Source Serif 4'), + url("SourceSerif4-Regular.ttf.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Source Serif 4'; + font-style: italic; + font-weight: 400; + src: local('Source Serif 4 Italic'), + url("SourceSerif4-It.ttf.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Source Serif 4'; + font-style: normal; + font-weight: 700; + src: local('Source Serif 4 Bold'), + url("SourceSerif4-Bold.ttf.woff2") format("woff2"); + font-display: swap; +} + +/* See SourceCodePro-LICENSE.txt for the Source Code Pro license. */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 400; + /* Avoid using locally installed font because bad versions are in circulation: + * see https://github.com/rust-lang/rust/issues/24355 */ + src: url("SourceCodePro-Regular.ttf.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Source Code Pro'; + font-style: italic; + font-weight: 400; + src: url("SourceCodePro-It.ttf.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 600; + src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2"); + font-display: swap; +} + +/* Avoid using legacy CJK serif fonts in Windows like Batang. */ +@font-face { + font-family: 'NanumBarunGothic'; + src: url("NanumBarunGothic.ttf.woff2") format("woff2"); + font-display: swap; + unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF; +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* This part handles the "default" theme being used depending on the system one. */ +html { + content: ""; +} +@media (prefers-color-scheme: light) { + html { + content: "light"; + } +} +@media (prefers-color-scheme: dark) { + html { + content: "dark"; + } +} + +/* General structure and fonts */ + +body { + /* Line spacing at least 1.5 per Web Content Accessibility Guidelines + https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */ + font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif; + margin: 0; + position: relative; + /* We use overflow-wrap: break-word for Safari, which doesn't recognize + `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */ + overflow-wrap: break-word; + /* Then override it with `anywhere`, which is required to make non-Safari browsers break + more aggressively when we want them to. */ + overflow-wrap: anywhere; + + -webkit-font-feature-settings: "kern", "liga"; + -moz-font-feature-settings: "kern", "liga"; + font-feature-settings: "kern", "liga"; + + background-color: var(--main-background-color); + color: var(--main-color); +} + +h1 { + font-size: 1.5rem; /* 24px */ +} +h2 { + font-size: 1.375rem; /* 22px */ +} +h3 { + font-size: 1.25rem; /* 20px */ +} +h1, h2, h3, h4, h5, h6 { + font-weight: 500; +} +h1, h2, h3, h4 { + margin: 20px 0 15px 0; + padding-bottom: 6px; +} +.docblock h3, .docblock h4, h5, h6 { + margin: 15px 0 5px 0; +} +h1.fqn { + margin: 0; + padding: 0; + border-bottom-color: var(--headings-border-bottom-color); +} +h2, h3, h4 { + border-bottom-color: var(--headings-border-bottom-color); +} +.main-heading { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding-bottom: 6px; + margin-bottom: 15px; +} +.main-heading a:hover { + text-decoration: underline; +} +#toggle-all-docs { + text-decoration: none; +} +/* The only headings that get underlines are: + Markdown-generated headings within the top-doc + Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc) + Underlines elsewhere in the documentation break up visual flow and tend to invert + section hierarchies. */ +h2, +.top-doc .docblock > h3, +.top-doc .docblock > h4 { + border-bottom: 1px solid var(--headings-border-bottom-color); +} +h3.code-header { + font-size: 1.125rem; /* 18px */ +} +h4.code-header { + font-size: 1rem; +} +.code-header { + font-weight: 600; + border-bottom-style: none; + margin: 0; + padding: 0; + margin-top: 0.6em; + margin-bottom: 0.4em; +} +.impl, +.impl-items .method, +.methods .method, +.impl-items .type, +.methods .type, +.impl-items .associatedconstant, +.methods .associatedconstant, +.impl-items .associatedtype, +.methods .associatedtype { + flex-basis: 100%; + font-weight: 600; + position: relative; +} + +div.impl-items > div { + padding-left: 0; +} + +h1, h2, h3, h4, h5, h6, +.sidebar, +.mobile-topbar, +a.source, +.search-input, +.search-results .result-name, +.content table td:first-child > a, +.item-left > a, +.out-of-band, +span.since, +#source-sidebar, #sidebar-toggle, +details.rustdoc-toggle > summary::before, +div.impl-items > div:not(.docblock):not(.item-info), +.content ul.crate a.crate, +a.srclink, +#main-content > .since, +#help-button > button, +details.rustdoc-toggle.top-doc > summary, +details.rustdoc-toggle.top-doc > summary::before, +details.rustdoc-toggle.non-exhaustive > summary, +details.rustdoc-toggle.non-exhaustive > summary::before, +.scraped-example-title, +.more-examples-toggle summary, .more-examples-toggle .hide-more, +.example-links a, +/* This selector is for the items listed in the "all items" page. */ +#main-content > ul.docblock > li > a { + font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; +} + +h1, h2, h3, h4, +a#toggle-all-docs, +a.anchor, +.small-section-header a, +#source-sidebar a, +pre.rust a, +.sidebar h2 a, +.sidebar h3 a, +.mobile-topbar h2 a, +.in-band a, +.search-results a, +.module-item .stab, +.import-item .stab, +.result-name .primitive > i, .result-name .keyword > i, +.content .method .where, +.content .fn .where, +.content .where.fmt-newline { + color: var(--main-color); +} + +ol, ul { + padding-left: 24px; +} +ul ul, ol ul, ul ol, ol ol { + margin-bottom: .625em; +} + +p { + /* 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 */ + margin: 0 0 .75em 0; +} + +summary { + outline: none; +} + +/* Fix some style changes due to normalize.css 8 */ + +td, +th { + padding: 0; +} + +table { + border-collapse: collapse; +} + +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} + +button { + /* Buttons on Safari have different default padding than other platforms. Make them the same. */ + padding: 1px 6px; +} + +/* end tweaks for normalize.css 8 */ + +.rustdoc { + display: flex; + flex-direction: row; + flex-wrap: nowrap; +} + +main { + position: relative; + flex-grow: 1; + padding: 10px 15px 40px 45px; + min-width: 0; +} + +.source main { + padding: 15px; +} + +.width-limiter { + max-width: 960px; + margin-right: auto; +} + +.source .width-limiter { + max-width: unset; +} + +details:not(.rustdoc-toggle) summary { + margin-bottom: .6em; +} + +code, pre, a.test-arrow, .code-header { + font-family: "Source Code Pro", monospace; +} +.docblock code, .docblock-short code { + border-radius: 3px; + padding: 0 0.125em; +} +.docblock pre code, .docblock-short pre code { + padding: 0; +} +pre { + padding: 14px; +} +.docblock.item-decl { + margin-left: 0; +} +.item-decl pre { + overflow-x: auto; +} + +.source .content pre { + padding: 20px; +} + +img { + max-width: 100%; +} + +li { + position: relative; +} + +.source .content { + max-width: none; + overflow: visible; + margin-left: 0px; +} + +nav.sub { + position: relative; + font-size: 1rem; +} + +.sub-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; +} + +.sub-logo-container { + display: none; + margin-right: 20px; +} + +.source .sub-logo-container { + display: block; +} + +.source .sub-logo-container > img { + height: 60px; + width: 60px; + object-fit: contain; +} + +.sidebar, .mobile-topbar, .sidebar-menu-toggle { + background-color: var(--sidebar-background-color); +} + +.sidebar { + font-size: 0.875rem; + width: 250px; + min-width: 200px; + overflow-y: scroll; + position: sticky; + height: 100vh; + top: 0; + left: 0; +} + +.sidebar-elems, +.sidebar > .location { + padding-left: 24px; +} + +.sidebar .location { + overflow-wrap: anywhere; +} + +.rustdoc.source .sidebar { + width: 50px; + min-width: 0px; + max-width: 300px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; + border-right: 1px solid; + overflow-x: hidden; + /* The sidebar is by default hidden */ + overflow-y: hidden; +} + +.rustdoc.source .sidebar .sidebar-logo { + display: none; +} + +.source .sidebar, #sidebar-toggle, #source-sidebar { + background-color: var(--sidebar-background-color); +} + +#sidebar-toggle > button:hover, #sidebar-toggle > button:focus { + background-color: var(--sidebar-background-color-hover); +} + +.source .sidebar > *:not(#sidebar-toggle) { + opacity: 0; + visibility: hidden; +} + +.source-sidebar-expanded .source .sidebar { + overflow-y: auto; +} + +.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) { + opacity: 1; + visibility: visible; +} + +#all-types { + margin-top: 1em; +} + +/* Improve the scrollbar display on firefox */ +* { + scrollbar-width: initial; + scrollbar-color: var(--scrollbar-color); +} +.sidebar { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-color); +} + +/* Improve the scrollbar display on webkit-based browsers */ +::-webkit-scrollbar { + width: 12px; +} +.sidebar::-webkit-scrollbar { + width: 8px; +} +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0; + background-color: var(--scrollbar-track-background-color); +} +.sidebar::-webkit-scrollbar-track { + background-color: var(--scrollbar-track-background-color); +} +::-webkit-scrollbar-thumb, .sidebar::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-thumb-background-color); +} + +/* Everything else */ + +.hidden { + display: none !important; +} + +.sidebar .logo-container { + display: flex; + margin-top: 10px; + margin-bottom: 10px; + justify-content: center; +} + +.version { + overflow-wrap: break-word; +} + +.logo-container > img { + height: 100px; + width: 100px; +} + +.location:empty { + border: none; +} + +.location a:first-of-type { + font-weight: 500; +} + +.block { + padding: 0; +} +.block ul, .block li { + padding: 0; + margin: 0; + list-style: none; +} + +.block a, +h2.location a { + display: block; + padding: 0.25rem; + margin-left: -0.25rem; + + text-overflow: ellipsis; + overflow: hidden; +} + +.sidebar h2 { + border-bottom: none; + font-weight: 500; + padding: 0; + margin: 0; + margin-top: 0.7rem; + margin-bottom: 0.7rem; +} + +.sidebar h3 { + font-size: 1.125rem; /* 18px */ + font-weight: 500; + padding: 0; + margin: 0; +} + +.sidebar-elems .block { + margin-bottom: 2em; +} + +.sidebar-elems .block li a { + white-space: nowrap; +} + +.mobile-topbar { + display: none; +} + +.source .content pre.rust { + white-space: pre; + overflow: auto; + padding-left: 0; +} + +.rustdoc .example-wrap { + display: inline-flex; + margin-bottom: 10px; +} + +.example-wrap { + position: relative; + width: 100%; +} + +.example-wrap > pre.line-number { + overflow: initial; + border: 1px solid; + padding: 13px 8px; + text-align: right; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} + +.example-wrap > pre.rust a:hover { + text-decoration: underline; +} + +.line-numbers { + text-align: right; +} +.rustdoc:not(.source) .example-wrap > pre:not(.line-number) { + width: 100%; + overflow-x: auto; +} + +.rustdoc:not(.source) .example-wrap > pre.line-numbers { + width: auto; + overflow-x: visible; +} + +.rustdoc .example-wrap > pre { + margin: 0; +} + +#search { + position: relative; +} + +.search-loading { + text-align: center; +} + +#results > table { + width: 100%; + table-layout: fixed; +} + +.content > .example-wrap pre.line-numbers { + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.line-numbers span { + cursor: pointer; +} + +.docblock-short { + overflow-wrap: break-word; + overflow-wrap: anywhere; +} +.docblock-short p { + display: inline; +} + +.docblock-short p { + overflow: hidden; + text-overflow: ellipsis; + margin: 0; +} +/* Wrap non-pre code blocks (`text`) but not (```text```). */ +.docblock > :not(pre) > code, +.docblock-short > :not(pre) > code { + white-space: pre-wrap; +} + +.top-doc .docblock h2 { font-size: 1.375rem; } +.top-doc .docblock h3 { font-size: 1.25rem; } +.top-doc .docblock h4, +.top-doc .docblock h5 { + font-size: 1.125rem; +} +.top-doc .docblock h6 { + font-size: 1rem; +} + +.docblock h5 { font-size: 1rem; } +.docblock h6 { font-size: 0.875rem; } +.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 { + border-bottom-color: var(--headings-border-bottom-color); +} + +.docblock { + margin-left: 24px; + position: relative; +} + +.docblock > :not(.information):not(.more-examples-toggle) { + max-width: 100%; + overflow-x: auto; +} + +.content .out-of-band { + flex-grow: 0; + font-size: 1.125rem; + font-weight: normal; + float: right; +} + +.method > .code-header, .trait-impl > .code-header { + max-width: calc(100% - 41px); + display: block; +} + +.content .in-band { + flex-grow: 1; + margin: 0px; + padding: 0px; + overflow-wrap: break-word; + overflow-wrap: anywhere; +} + +.in-band > code, .in-band > .code-header { + display: inline-block; +} + +.docblock code, .docblock-short code, +pre, .rustdoc.source .example-wrap { + background-color: var(--code-block-background-color); +} + +#main-content { + position: relative; +} +#main-content > .since { + top: inherit; +} + +.content table:not(.table-display) { + border-spacing: 0 5px; +} +.content td { vertical-align: top; } +.content td:first-child { padding-right: 20px; } +.content td p:first-child { margin-top: 0; } +.content td h1, .content td h2 { margin-left: 0; font-size: 1.125rem; } +.content tr:first-child td { border-top: 0; } + +.docblock table { + margin: .5em 0; + width: calc(100% - 2px); + overflow-x: auto; + display: block; +} + +.docblock table td { + padding: .5em; + border: 1px dashed; +} + +.docblock table th { + padding: .5em; + text-align: left; + border: 1px solid; +} + +.fields + table { + margin-bottom: 1em; +} + +.content .item-list { + list-style-type: none; + padding: 0; +} + +.content .multi-column { + -moz-column-count: 5; + -moz-column-gap: 2.5em; + -webkit-column-count: 5; + -webkit-column-gap: 2.5em; + column-count: 5; + column-gap: 2.5em; +} +.content .multi-column li { width: 100%; display: inline-block; } + +.content > .methods > .method { + font-size: 1rem; + position: relative; +} +/* Shift "where ..." part of method or fn definition down a line */ +.content .method .where, +.content .fn .where, +.content .where.fmt-newline { + display: block; + font-size: 0.875rem; +} + +.content .methods > div:not(.notable-traits):not(.method) { + margin-left: 40px; + margin-bottom: 15px; +} + +.content .docblock > .impl-items { + margin-left: 20px; + margin-top: -34px; +} +.content .docblock >.impl-items .table-display { + margin: 0; +} +.content .docblock >.impl-items table td { + padding: 0; +} +.content .docblock > .impl-items .table-display, .impl-items table td { + border: none; +} + +.item-info { + display: block; +} + +.content .item-info code { + font-size: 0.875rem; +} + +.content .item-info { + position: relative; + margin-left: 24px; +} + +.sub-variant > div > .item-info { + margin-top: initial; +} + +.content .impl-items .docblock, .content .impl-items .item-info { + margin-bottom: .6em; +} + +.content .impl-items > .item-info { + margin-left: 40px; +} + +.methods > .item-info, .content .impl-items > .item-info { + margin-top: -8px; +} + +.impl-items { + flex-basis: 100%; +} + +#main-content > .item-info { + margin-top: 0; + margin-left: 0; +} + +nav.sub { + flex-grow: 1; + margin-bottom: 25px; +} +.source nav.sub { + margin-left: 32px; +} +nav.main { + padding: 20px 0; + text-align: center; +} +nav.main .current { + border-top: 1px solid; + border-bottom: 1px solid; +} +nav.main .separator { + border: 1px solid; + display: inline-block; + height: 23px; + margin: 0 20px; +} +nav.sum { text-align: right; } +nav.sub form { display: inline; } + +a { + text-decoration: none; + background: transparent; +} + +.small-section-header { + display: flex; + justify-content: space-between; + position: relative; +} + +.small-section-header:hover > .anchor { + display: initial; +} + +.in-band:hover > .anchor, .impl:hover > .anchor, .method.trait-impl:hover > .anchor, +.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor, +.associatedtype.trait-impl:hover > .anchor { + display: inline-block; + position: absolute; +} +.anchor { + display: none; + position: absolute; + left: -0.5em; + background: none !important; +} +.anchor.field { + left: -5px; +} +.small-section-header > .anchor { + left: -15px; + padding-right: 8px; +} +h2.small-section-header > .anchor { + padding-right: 6px; +} +.anchor::before { + content: '§'; +} + +.docblock a:not(.srclink):not(.test-arrow):not(.scrape-help):hover, +.docblock-short a:not(.srclink):not(.test-arrow):not(.scrape-help):hover, .item-info a { + text-decoration: underline; +} + +.block a.current.crate { font-weight: 500; } + +/* In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap + as much as needed on mobile (see + src/test/rustdoc-gui/type-declaration-overflow.goml for an example of why + this matters). The `anywhere` value means: + + "Soft wrap opportunities introduced by the word break are considered when + calculating min-content intrinsic sizes." + + https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap#values + + For table layouts, that becomes a problem: the browser tries to make each + column as narrow as possible, and `overflow-wrap: anywhere` means it can do + so by breaking words - even if some other column could be shrunk without + breaking words! This shows up, for instance, in the `Structs` / `Modules` / + `Functions` (etcetera) sections of a module page, and when a docblock + contains a table. + + So, for table layouts, override the default with break-word, which does + _not_ affect min-content intrinsic sizes. +*/ +table, +.item-table { + overflow-wrap: break-word; +} + +.item-table { + display: table; +} +.item-row { + display: table-row; +} +.item-left, .item-right { + display: table-cell; +} +.item-left { + padding-right: 1.25rem; +} + +.search-container { + position: relative; + display: flex; + height: 34px; +} +.search-container > * { + height: 100%; +} +.search-results-title { + display: inline; +} +#search-settings { + font-size: 1.5rem; + font-weight: 500; + margin-bottom: 20px; +} +#crate-search { + min-width: 115px; + margin-top: 5px; + padding-left: 0.15em; + padding-right: 23px; + border: 1px solid; + border-radius: 4px; + outline: none; + cursor: pointer; + -moz-appearance: none; + -webkit-appearance: none; + /* Removes default arrow from firefox */ + background-repeat: no-repeat; + background-color: transparent; + background-size: 20px; + background-position: calc(100% - 1px) 56%; + background-image: /* AUTOREPLACE: */url("down-arrow.svg"); + max-width: 100%; + text-overflow: ellipsis; +} +.search-container { + margin-top: 4px; +} +.search-input { + /* Override Normalize.css: it has a rule that sets + -webkit-appearance: textfield for search inputs. That + causes rounded corners and no border on iOS Safari. */ + -webkit-appearance: none; + /* Override Normalize.css: we have margins and do + not want to overflow - the `moz` attribute is necessary + until Firefox 29, too early to drop at this point */ + -moz-box-sizing: border-box !important; + box-sizing: border-box !important; + outline: none; + border: 1px solid; + border-radius: 2px; + padding: 8px; + font-size: 1rem; + width: 100%; +} + +.search-results { + display: none; + padding-bottom: 2em; +} + +.search-results.active { + display: block; + /* prevent overhanging tabs from moving the first result */ + clear: both; +} + +.search-results .desc > span { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: block; +} + +.search-results > a { + display: block; + width: 100%; + /* A little margin ensures the browser's outlining of focused links has room to display. */ + margin-left: 2px; + margin-right: 2px; + border-bottom: 1px solid #aaa3; +} + +.search-results > a > div { + display: flex; + flex-flow: row wrap; +} + +.search-results .result-name, .search-results div.desc, .search-results .result-description { + width: 50%; +} +.search-results .result-name { + padding-right: 1em; +} + +.search-results .result-name > span { + display: inline-block; + margin: 0; + font-weight: normal; +} + +.popover { + font-size: 1rem; + position: absolute; + right: 0; + z-index: 2; + display: block; + margin-top: 7px; + border-radius: 3px; + border: 1px solid; + font-size: 1rem; +} + +/* This rule is to draw the little arrow connecting the settings menu to the gear icon. */ +.popover::before { + content: ''; + position: absolute; + right: 11px; + border: solid; + border-width: 1px 1px 0 0; + display: inline-block; + padding: 4px; + transform: rotate(-45deg); + top: -5px; +} + +.popover, .popover::before { + background-color: var(--main-background-color); + color: var(--main-color); +} + +#help-button .popover { + max-width: 600px; +} + +#help-button .popover::before { + right: 48px; +} + +#help-button dt { + float: left; + clear: left; + display: block; + margin-right: 0.5rem; +} +#help-button span.top, #help-button span.bottom { + text-align: center; + display: block; + font-size: 1.125rem; +} +#help-button span.top { + text-align: center; + display: block; + margin: 10px 0; + border-bottom: 1px solid; + padding-bottom: 4px; + margin-bottom: 6px; +} +#help-button span.bottom { + clear: both; + border-top: 1px solid; +} +.side-by-side { + text-align: initial; +} +.side-by-side > div { + width: 50%; + float: left; + padding: 0 20px 20px 17px; +} + +.item-info .stab { + width: fit-content; + /* This min-height is needed to unify the height of the stab elements because some of them + have emojis. + */ + min-height: 36px; + display: flex; + align-items: center; + white-space: pre-wrap; +} +.stab { + padding: 3px; + margin-bottom: 5px; + font-size: 0.875rem; + font-weight: normal; +} +.stab p { + display: inline; + margin: 0; +} + +.stab .emoji { + font-size: 1.25rem; +} + +/* Black one-pixel outline around emoji shapes */ +.emoji { + text-shadow: + 1px 0 0 black, + -1px 0 0 black, + 0 1px 0 black, + 0 -1px 0 black; +} + +.module-item .stab, +.import-item .stab { + border-radius: 3px; + display: inline-block; + font-size: 0.875rem; + line-height: 1.2; + margin-bottom: 0; + margin-left: 0.3125em; + padding: 2px; + vertical-align: text-bottom; +} + +.module-item.unstable, +.import-item.unstable { + opacity: 0.65; +} + +.since { + font-weight: normal; + font-size: initial; +} + +.rightside { + padding-left: 12px; + padding-right: 2px; + position: initial; +} + +.impl-items .srclink, .impl .srclink, .methods .srclink { + /* Override header settings otherwise it's too bold */ + font-weight: normal; + font-size: 1rem; +} + +.rightside { + float: right; +} + +.variants_table { + width: 100%; +} + +.variants_table tbody tr td:first-child { + width: 1%; /* make the variant name as small as possible */ +} + +td.summary-column { + width: 100%; +} + +.summary { + padding-right: 0px; +} + +pre.rust .question-mark { + font-weight: bold; +} + +a.test-arrow { + display: inline-block; + visibility: hidden; + position: absolute; + padding: 5px 10px 5px 10px; + border-radius: 5px; + font-size: 1.375rem; + top: 5px; + right: 5px; + z-index: 1; +} +.example-wrap:hover .test-arrow { + visibility: visible; +} +a.test-arrow:hover{ + text-decoration: none; +} + +.code-attribute { + font-weight: 300; +} + +.item-spacer { + width: 100%; + height: 12px; +} + +.out-of-band > span.since { + position: initial; + font-size: 1.25rem; +} + +h3.variant { + font-weight: 600; + font-size: 1.125rem; + margin-bottom: 10px; + border-bottom: none; +} + +.sub-variant h4 { + font-size: 1rem; + font-weight: 400; + border-bottom: none; + margin-top: 0; + margin-bottom: 0; +} + +.sub-variant { + margin-left: 24px; + margin-bottom: 40px; +} + +.sub-variant > .sub-variant-field { + margin-left: 24px; +} + +.toggle-label { + display: inline-block; + margin-left: 4px; + margin-top: 3px; +} + +:target > code, :target > .code-header { + opacity: 1; +} + +:target { + padding-right: 3px; +} + +.information { + position: absolute; + left: -25px; + margin-top: 7px; + z-index: 1; +} + +.tooltip { + position: relative; + display: inline-block; + cursor: pointer; +} + +.tooltip::after { + display: none; + text-align: center; + padding: 5px 3px 3px 3px; + border-radius: 6px; + margin-left: 5px; + font-size: 1rem; +} + +.tooltip.ignore::after { + content: "This example is not tested"; +} +.tooltip.compile_fail::after { + content: "This example deliberately fails to compile"; +} +.tooltip.should_panic::after { + content: "This example panics"; +} +.tooltip.edition::after { + content: "This code runs with edition " attr(data-edition); +} + +.tooltip::before { + content: " "; + position: absolute; + top: 50%; + left: 16px; + margin-top: -5px; + border-width: 5px; + border-style: solid; + display: none; +} + +.tooltip:hover::before, .tooltip:hover::after { + display: inline; +} + +.tooltip.compile_fail, .tooltip.should_panic, .tooltip.ignore { + font-weight: bold; + font-size: 1.25rem; +} + +.notable-traits-tooltip { + display: inline-block; + cursor: pointer; +} + +.notable-traits:hover .notable-traits-tooltiptext, +.notable-traits .notable-traits-tooltiptext.force-tooltip { + display: inline-block; +} + +.notable-traits .notable-traits-tooltiptext { + display: none; + padding: 5px 3px 3px 3px; + border-radius: 6px; + margin-left: 5px; + z-index: 10; + font-size: 1rem; + cursor: default; + position: absolute; + border: 1px solid; +} + +.notable-traits-tooltip::after { + /* The margin on the tooltip does not capture hover events, + this extends the area of hover enough so that mouse hover is not + lost when moving the mouse to the tooltip */ + content: "\00a0\00a0\00a0"; +} + +.notable-traits .notable, .notable-traits .docblock { + margin: 0; +} + +.notable-traits .notable { + margin: 0; + margin-bottom: 13px; + font-size: 1.1875rem; + font-weight: 600; + display: block; +} + +.notable-traits .docblock code.content{ + margin: 0; + padding: 0; + font-size: 1.25rem; +} + +/* Example code has the "Run" button that needs to be positioned relative to the pre */ +pre.rust.rust-example-rendered { + position: relative; +} + +pre.rust { + tab-size: 4; + -moz-tab-size: 4; +} + +.search-failed { + text-align: center; + margin-top: 20px; + display: none; +} + +.search-failed.active { + display: block; +} + +.search-failed > ul { + text-align: left; + max-width: 570px; + margin-left: auto; + margin-right: auto; +} + +#titles { + height: 35px; +} + +#titles > button { + float: left; + width: 33.3%; + text-align: center; + font-size: 1.125rem; + cursor: pointer; + border: 0; + border-top: 2px solid; +} + +#titles > button:first-child:last-child { + margin-right: 1px; + width: calc(100% - 1px); +} + +#titles > button:not(:last-child) { + margin-right: 1px; + width: calc(33.3% - 1px); +} + +#titles > button > div.count { + display: inline-block; + font-size: 1rem; +} + +.notable-traits { + cursor: pointer; + z-index: 2; + margin-left: 5px; +} + +#sidebar-toggle { + position: sticky; + top: 0; + left: 0; + font-weight: bold; + font-size: 1.25rem; + border-bottom: 1px solid; + display: flex; + height: 40px; + justify-content: center; + align-items: center; + z-index: 10; +} +#source-sidebar { + width: 100%; + z-index: 1; + overflow: auto; +} +#source-sidebar > .title { + font-size: 1.5rem; + text-align: center; + border-bottom: 1px solid; + margin-bottom: 6px; +} +#sidebar-toggle > button { + background: none; + color: inherit; + cursor: pointer; + text-align: center; + border: none; + outline: none; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + /* work around button layout strangeness: https://stackoverflow.com/q/7271561 */ + width: 100%; + /* iOS button gradient: https://stackoverflow.com/q/5438567 */ + -webkit-appearance: none; + opacity: 1; +} +#settings-menu, #help-button { + margin-left: 4px; + outline: none; +} + +#copy-path { + height: 34px; +} +#settings-menu > a, #help-button > button, #copy-path { + padding: 5px; + width: 33px; + border: 1px solid; + border-radius: 2px; + cursor: pointer; +} +#settings-menu { + padding: 0; +} +#settings-menu > a, #help-button > button { + padding: 5px; + height: 100%; + display: block; +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} +#settings-menu.rotate > a img { + animation: rotating 2s linear infinite; +} + +.setting-line .radio-line input:checked { + box-shadow: inset 0 0 0 3px var(--main-background-color); + background-color: var(--settings-input-color); +} +.setting-line .radio-line input:focus { + box-shadow: 0 0 1px 1px var(--settings-input-color); +} +/* In here we combine both `:focus` and `:checked` properties. */ +.setting-line .radio-line input:checked:focus { + box-shadow: inset 0 0 0 3px var(--main-background-color), + 0 0 2px 2px var(--settings-input-color); +} +.setting-line .radio-line input:hover { + border-color: var(--settings-input-color) !important; +} +input:checked + .slider { + background-color: var(--settings-input-color); +} + +#help-button > button { + text-align: center; + /* Rare exception to specifying font sizes in rem. Since this is acting + as an icon, it's okay to specify their sizes in pixels. */ + font-size: 20px; + padding-top: 2px; +} + +#copy-path { + background: initial; + margin-left: 10px; + padding: 0; + padding-left: 2px; + border: 0; +} + +#theme-choices { + display: none; + position: absolute; + left: 0; + top: 28px; + border: 1px solid; + border-radius: 3px; + z-index: 1; + cursor: pointer; +} + +#theme-choices > button { + border: none; + width: 100%; + padding: 4px 8px; + text-align: center; + background: rgba(0,0,0,0); + overflow-wrap: normal; +} + +#theme-choices > button:not(:first-child) { + border-top: 1px solid; +} + +kbd { + display: inline-block; + padding: 3px 5px; + font: 15px monospace; + line-height: 10px; + vertical-align: middle; + border: solid 1px; + border-radius: 3px; + cursor: default; +} + +.hidden-by-impl-hider, +.hidden-by-usual-hider { + /* important because of conflicting rule for small screens */ + display: none !important; +} + +#implementations-list > h3 > span.in-band { + width: 100%; +} + +.table-display { + width: 100%; + border: 0; + border-collapse: collapse; + border-spacing: 0; + font-size: 1rem; +} + +.table-display tr td:first-child { + padding-right: 0; +} + +.table-display tr td:last-child { + float: right; +} +.table-display .out-of-band { + position: relative; + font-size: 1.125rem; + display: block; +} + +.table-display td:hover .anchor { + display: block; + top: 2px; + left: -5px; +} + +#main-content > ul { + padding-left: 10px; +} +#main-content > ul > li { + list-style: none; +} + +.non-exhaustive { + margin-bottom: 1em; +} + +details.dir-entry { + padding-left: 4px; +} + +details.dir-entry > summary { + margin: 0 0 0 13px; + list-style-position: outside; + cursor: pointer; +} + +details.dir-entry div.folders, details.dir-entry div.files { + padding-left: 23px; +} + +details.dir-entry a { + display: block; +} + +/* The hideme class is used on summary tags that contain a span with + placeholder text shown only when the toggle is closed. For instance, + "Expand description" or "Show methods". */ +details.rustdoc-toggle > summary.hideme { + cursor: pointer; +} + +details.rustdoc-toggle > summary { + list-style: none; +} +details.rustdoc-toggle > summary::-webkit-details-marker, +details.rustdoc-toggle > summary::marker { + display: none; +} + +details.rustdoc-toggle > summary.hideme > span { + margin-left: 9px; +} + +details.rustdoc-toggle > summary::before { + content: ""; + cursor: pointer; + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-position: top left; + display: inline-block; + vertical-align: middle; + opacity: .5; +} + +/* Screen readers see the text version at the end the line. + Visual readers see the icon at the start of the line, but small and transparent. */ +details.rustdoc-toggle > summary::after { + content: "Expand"; + overflow: hidden; + width: 0; + height: 0; + position: absolute; +} + +details.rustdoc-toggle > summary.hideme::after { + /* "hideme" toggles already have a description when they're contracted */ + content: ""; +} + +details.rustdoc-toggle > summary:focus::before, +details.rustdoc-toggle > summary:hover::before { + opacity: 1; +} + +details.rustdoc-toggle.top-doc > summary, +details.rustdoc-toggle.top-doc > summary::before, +details.rustdoc-toggle.non-exhaustive > summary, +details.rustdoc-toggle.non-exhaustive > summary::before { + font-size: 1rem; +} + +details.non-exhaustive { + margin-bottom: 8px; +} + +details.rustdoc-toggle > summary.hideme::before { + position: relative; +} + +details.rustdoc-toggle > summary:not(.hideme)::before { + position: absolute; + left: -24px; + top: 4px; +} + +.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before { + position: absolute; + left: -24px; +} + +/* When a "hideme" summary is open and the "Expand description" or "Show + methods" text is hidden, we want the [-] toggle that remains to not + affect the layout of the items to its right. To do that, we use + absolute positioning. Note that we also set position: relative + on the parent <details> to make this work properly. */ +details.rustdoc-toggle[open] > summary.hideme { + position: absolute; +} + +details.rustdoc-toggle { + position: relative; +} + +details.rustdoc-toggle[open] > summary.hideme > span { + display: none; +} + +details.rustdoc-toggle[open] > summary::before, +details.rustdoc-toggle[open] > summary.hideme::before { + background-image: /* AUTOREPLACE: */url("toggle-minus.svg"); +} + +details.rustdoc-toggle > summary::before { + background-image: /* AUTOREPLACE: */url("toggle-plus.svg"); +} + +details.rustdoc-toggle[open] > summary::before, +details.rustdoc-toggle[open] > summary.hideme::before { + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-position: top left; + display: inline-block; + content: ""; +} + +details.rustdoc-toggle[open] > summary::after, +details.rustdoc-toggle[open] > summary.hideme::after { + content: "Collapse"; +} + +/* This is needed in docblocks to have the "▶" element to be on the same line. */ +.docblock summary > * { + display: inline-block; +} + +/* Media Queries */ + +/* +WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY; +If you update this line, then you also need to update the line with the same warning +in storage.js plus the media query with (max-width: 700px) +*/ +@media (min-width: 701px) { + /* In case there is no documentation before a code block, we need to add some margin at the top + to prevent an overlay between the "collapse toggle" and the information tooltip. + However, it's not needed with smaller screen width because the doc/code block is always put + "one line" below. */ + .docblock > .information:first-child > .tooltip { + margin-top: 16px; + } + + /* When we expand the sidebar on the source code page, we hide the logo on the left of the + search bar to have more space. */ + .source-sidebar-expanded .source .sidebar + main .width-limiter .sub-logo-container.rust-logo { + display: none; + } + + .source-sidebar-expanded .source .sidebar { + width: 300px; + } +} + +/* +WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY +If you update this line, then you also need to update the line with the same warning +in storage.js plus the media query with (min-width: 701px) +*/ +@media (max-width: 700px) { + /* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, + or visiting a URL with a fragment like `#method.new`, we don't want the item to be obscured + by the topbar. Anything with an `id` gets scroll-margin-top equal to .mobile-topbar's size. + */ + *[id] { + scroll-margin-top: 45px; + } + + .rustdoc { + padding-top: 0px; + /* Sidebar should overlay main content, rather than pushing main content to the right. + Turn off `display: flex` on the body element. */ + display: block; + } + + main { + padding-left: 15px; + padding-top: 0px; + } + + .rustdoc, + .main-heading { + flex-direction: column; + } + + .content .out-of-band { + text-align: left; + margin-left: initial; + padding: initial; + } + + .content .out-of-band .since::before { + content: "Since "; + } + + #copy-path { + display: none; + } + + /* Hide the logo and item name from the sidebar. Those are displayed + in the mobile-topbar instead. */ + .sidebar .sidebar-logo, + .sidebar .location { + display: none; + } + + .sidebar-elems { + margin-top: 1em; + } + + .sidebar { + position: fixed; + top: 45px; + /* Hide the sidebar offscreen while not in use. Doing this instead of display: none means + the sidebar stays visible for screen readers, which is useful for navigation. */ + left: -1000px; + margin-left: 0; + margin: 0; + padding: 0; + z-index: 11; + /* Reduce height slightly to account for mobile topbar. */ + height: calc(100vh - 45px); + } + + /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, + so don't bump down the main content or the sidebar. */ + .source main, + .rustdoc.source .sidebar { + top: 0; + padding: 0; + height: 100vh; + border: 0; + } + + .sidebar.shown, + .source-sidebar-expanded .source .sidebar, + .sidebar:focus-within { + left: 0; + } + + .rustdoc.source > .sidebar { + position: fixed; + margin: 0; + z-index: 11; + width: 0; + } + + .mobile-topbar .location a { + padding: 0; + margin: 0; + } + + .mobile-topbar .location { + border: none; + padding: 0; + margin: auto 0.5em auto auto; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + /* Rare exception to specifying font sizes in rem. Since the topbar + height is specified in pixels, this also has to be specified in + pixels to avoid overflowing the topbar when the user sets a bigger + font size. */ + font-size: 24px; + } + + .mobile-topbar .logo-container { + max-height: 45px; + } + + .mobile-topbar .logo-container > img { + max-width: 35px; + max-height: 35px; + margin-left: 20px; + margin-top: 5px; + margin-bottom: 5px; + } + + .mobile-topbar { + display: flex; + flex-direction: row; + position: sticky; + z-index: 10; + font-size: 2rem; + height: 45px; + width: 100%; + left: 0; + top: 0; + } + + .source .mobile-topbar { + display: none; + } + + .sidebar-menu-toggle { + width: 45px; + /* Rare exception to specifying font sizes in rem. Since this is acting + as an icon, it's okay to specify its sizes in pixels. */ + font-size: 32px; + border: none; + } + + .sidebar-elems { + background-color: var(--sidebar-background-color); + } + + .source nav:not(.sidebar).sub { + margin-left: 32px; + } + + .content { + margin-left: 0px; + } + + .source .content { + margin-top: 10px; + } + + #search { + margin-left: 0; + padding: 0; + } + + .anchor { + display: none !important; + } + + .notable-traits { + position: absolute; + left: -22px; + top: 24px; + } + + #titles > button > div.count { + float: left; + width: 100%; + } + + #titles { + height: 50px; + } + + /* Because of ios, we need to actually have a full height sidebar title so the + * actual sidebar can show up. But then we need to make it transparent so we don't + * hide content. The filler just allows to create the background for the sidebar + * title. But because of the absolute position, I had to lower the z-index. + */ + #sidebar-filler { + position: fixed; + left: 45px; + width: calc(100% - 45px); + top: 0; + height: 45px; + z-index: -1; + border-bottom: 1px solid; + } + + #main-content > details.rustdoc-toggle > summary::before, + #main-content > div > details.rustdoc-toggle > summary::before { + left: -11px; + } + + #sidebar-toggle { + position: fixed; + left: 1px; + top: 100px; + width: 30px; + font-size: 1.5rem; + text-align: center; + padding: 0; + z-index: 10; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + cursor: pointer; + font-weight: bold; + border: 1px solid; + border-left: 0; + } + + .source-sidebar-expanded #sidebar-toggle { + left: unset; + top: unset; + width: unset; + border-top-right-radius: unset; + border-bottom-right-radius: unset; + position: sticky; + border: 0; + border-bottom: 1px solid; + } + + #source-sidebar { + z-index: 11; + } + + #main-content > .line-numbers { + margin-top: 0; + } + + .notable-traits .notable-traits-tooltiptext { + left: 0; + top: 100%; + } + + /* We don't display the help button on mobile devices. */ + #help-button { + display: none; + } + + /* Display an alternating layout on tablets and phones */ + .item-table { + display: block; + } + .item-row { + display: flex; + flex-flow: column wrap; + } + .item-left, .item-right { + width: 100%; + } + + /* Display an alternating layout on tablets and phones */ + .search-results > a { + border-bottom: 1px solid #aaa9; + padding: 5px 0px; + } + .search-results .result-name, .search-results div.desc, .search-results .result-description { + width: 100%; + } + .search-results div.desc, .search-results .result-description, .item-right { + padding-left: 2em; + } + + .source-sidebar-expanded .source .sidebar { + max-width: 100vw; + width: 100vw; + } + + /* Position of the "[-]" element. */ + details.rustdoc-toggle:not(.top-doc) > summary { + margin-left: 10px; + } + .impl-items > details.rustdoc-toggle > summary:not(.hideme)::before, + #main-content > details.rustdoc-toggle:not(.top-doc) > summary::before, + #main-content > div > details.rustdoc-toggle > summary::before { + left: -11px; + } +} + +@media print { + nav.sidebar, nav.sub, .content .out-of-band, a.srclink, #copy-path, + details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before, + details.rustdoc-toggle.top-doc > summary { + display: none; + } + + .docblock { + margin-left: 0; + } + + main { + padding: 10px; + } +} + +@media (max-width: 464px) { + #titles, #titles > button { + height: 73px; + } + + #main-content > table:not(.table-display) td { + word-break: break-word; + width: 50%; + } + + #crate-search { + border-radius: 4px; + } + + .docblock { + margin-left: 12px; + } + + .docblock code { + overflow-wrap: break-word; + overflow-wrap: anywhere; + } + + .sub-container { + flex-direction: column; + } + + .sub-logo-container { + align-self: center; + } + + .source .sub-logo-container > img { + height: 35px; + width: 35px; + } + + #sidebar-toggle { + top: 10px; + } + .source-sidebar-expanded #sidebar-toggle { + top: unset; + } +} + +.method-toggle summary, +.implementors-toggle summary, +.impl { + margin-bottom: 0.75em; +} + +.method-toggle[open] { + margin-bottom: 2em; +} + +.implementors-toggle[open] { + margin-bottom: 2em; +} + +#trait-implementations-list .method-toggle, +#synthetic-implementations-list .method-toggle, +#blanket-implementations-list .method-toggle { + margin-bottom: 1em; +} + +/* Begin: styles for --scrape-examples feature */ + +.scraped-example-list .scrape-help { + margin-left: 10px; + padding: 0 4px; + font-weight: normal; + font-size: 12px; + position: relative; + bottom: 1px; + background: transparent; + border-width: 1px; + border-style: solid; + border-radius: 50px; +} + +.scraped-example .code-wrapper { + position: relative; + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +.scraped-example:not(.expanded) .code-wrapper { + max-height: 240px; +} + +.scraped-example:not(.expanded) .code-wrapper pre { + overflow-y: hidden; + max-height: 240px; + padding-bottom: 0; +} + +.scraped-example:not(.expanded) .code-wrapper pre.line-numbers { + overflow-x: hidden; +} + +.scraped-example .code-wrapper .prev { + position: absolute; + top: 0.25em; + right: 2.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example .code-wrapper .next { + position: absolute; + top: 0.25em; + right: 1.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example .code-wrapper .expand { + position: absolute; + top: 0.25em; + right: 0.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example:not(.expanded) .code-wrapper:before { + content: " "; + width: 100%; + height: 5px; + position: absolute; + z-index: 100; + top: 0; +} + +.scraped-example:not(.expanded) .code-wrapper:after { + content: " "; + width: 100%; + height: 5px; + position: absolute; + z-index: 100; + bottom: 0; +} + +.scraped-example .code-wrapper .line-numbers { + margin: 0; + padding: 14px 0; +} + +.scraped-example .code-wrapper .line-numbers span { + padding: 0 14px; +} + +.scraped-example .code-wrapper .example-wrap { + flex: 1; + overflow-x: auto; + overflow-y: hidden; + margin-bottom: 0; +} + +.scraped-example:not(.expanded) .code-wrapper .example-wrap { + overflow-x: hidden; +} + +.scraped-example .code-wrapper .example-wrap pre.rust { + overflow-x: inherit; + width: inherit; + overflow-y: hidden; +} + + +.more-examples-toggle { + max-width: calc(100% + 25px); + margin-top: 10px; + margin-left: -25px; +} + +.more-examples-toggle .hide-more { + margin-left: 25px; + margin-bottom: 5px; + cursor: pointer; +} + +.more-scraped-examples { + margin-left: 5px; + display: flex; + flex-direction: row; +} + +.more-scraped-examples-inner { + /* 20px is width of toggle-line + toggle-line-inner */ + width: calc(100% - 20px); +} + +.toggle-line { + align-self: stretch; + margin-right: 10px; + margin-top: 5px; + padding: 0 4px; + cursor: pointer; +} + +.toggle-line-inner { + min-width: 2px; + height: 100%; +} + +.more-scraped-examples .scraped-example { + margin-bottom: 20px; +} + +.more-scraped-examples .scraped-example:last-child { + margin-bottom: 0; +} + +.example-links a { + margin-top: 20px; +} + +.example-links ul { + margin-bottom: 0; +} + +/* End: styles for --scrape-examples feature */ diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css new file mode 100644 index 000000000..e82ec0426 --- /dev/null +++ b/src/librustdoc/html/static/css/settings.css @@ -0,0 +1,90 @@ +.setting-line { + margin: 0.6em 0 0.6em 0.3em; + position: relative; +} + +.setting-line .choices { + display: flex; + flex-wrap: wrap; +} + +.setting-line .radio-line input { + margin-right: 0.3em; + height: 1.2rem; + width: 1.2rem; + border: 1px solid; + outline: none; + -webkit-appearance: none; + cursor: pointer; + border-radius: 50%; +} +.setting-line .radio-line input + span { + padding-bottom: 1px; +} + +.radio-line .setting-name { + width: 100%; +} + +.radio-line .choice { + margin-top: 0.1em; + margin-bottom: 0.1em; + min-width: 3.8em; + padding: 0.3em; + display: flex; + align-items: center; + cursor: pointer; +} +.radio-line .choice + .choice { + margin-left: 0.5em; +} + +.toggle { + position: relative; + width: 100%; + margin-right: 20px; + display: flex; + align-items: center; + cursor: pointer; +} + +.toggle input { + opacity: 0; + position: absolute; +} + +.slider { + position: relative; + width: 45px; + min-width: 45px; + display: block; + height: 28px; + margin-right: 20px; + cursor: pointer; + background-color: #ccc; + transition: .3s; +} + +.slider:before { + position: absolute; + content: ""; + height: 19px; + width: 19px; + left: 4px; + bottom: 4px; + transition: .3s; +} + +input:checked + .slider:before { + transform: translateX(19px); +} + +.setting-line > .sub-settings { + padding-left: 42px; + width: 100%; + display: block; +} + +#settings .setting-line { + margin: 1.2em 0.6em; +} diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css new file mode 100644 index 000000000..c42cac59b --- /dev/null +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -0,0 +1,563 @@ +/* +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; + --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; +} + +.slider { + background-color: #ccc; +} +.slider:before { + background-color: white; +} +input:focus + .slider { + box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); +} + +h1, h2, h3, h4 { + color: white; +} +h1.fqn a { + color: #fff; +} +h4 { + border: none; +} + +.in-band { + background-color: #0f1419; +} + +.docblock code { + color: #ffb454; +} +.code-header { + color: #e6e1cf; +} +.docblock pre > code, pre > code { + color: #e6e1cf; +} +span code { + color: #e6e1cf; +} +.docblock a > code { + color: #39AFD7 !important; +} +pre, .rustdoc.source .example-wrap { + color: #e6e1cf; +} + +.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); +} + +.sidebar .current, +.sidebar a:hover { + background-color: transparent; + color: #ffb44c; +} + +.sidebar-elems .location { + color: #ff7733; +} + +.line-numbers span { color: #5c6773; } +.line-numbers .line-highlighted { + color: #708090; + background-color: rgba(255, 236, 164, 0.06); + padding-right: 4px; + border-right: 1px solid #ffb44c; +} + +.docblock table td, .docblock table th { + border-color: #5c6773; +} + +.search-results a:hover { + background-color: #777; +} + +.search-results a:focus { + color: #000 !important; + background-color: #c6afb3; +} +.search-results a { + color: #0096cf; +} +.search-results a div.desc { + color: #c5c5c5; +} + +.content .item-info::before { color: #ccc; } + +.content span.foreigntype, .content a.foreigntype { color: #ffa0a5; } +.content span.union, .content a.union { color: #ffa0a5; } +.content span.constant, .content a.constant, +.content span.static, .content a.static { color: #39AFD7; } +.content span.primitive, .content a.primitive { color: #ffa0a5; } +.content span.traitalias, .content a.traitalias { color: #39AFD7; } +.content span.keyword, .content a.keyword { color: #39AFD7; } + +.content span.externcrate, .content span.mod, .content a.mod { + color: #39AFD7; +} +.content span.struct, .content a.struct { + color: #ffa0a5; +} +.content span.enum, .content a.enum { + color: #ffa0a5; +} +.content span.trait, .content a.trait { + color: #39AFD7; +} +.content span.type, .content a.type { + color: #39AFD7; +} +.content span.type, +.content a.type, +.block a.current.type { color: #39AFD7; } +.content span.associatedtype, +.content a.associatedtype, +.block a.current.associatedtype { color: #39AFD7; } +.content span.fn, .content a.fn, .content span.method, +.content a.method, .content span.tymethod, +.content a.tymethod, .content .fnname { + color: #fdd687; +} +.content span.attr, .content a.attr, .content span.derive, +.content a.derive, .content span.macro, .content a.macro { + color: #a37acc; +} + +.sidebar a { color: #53b1db; } +.sidebar a.current.type { color: #53b1db; } +.sidebar a.current.associatedtype { color: #53b1db; } + +pre.rust .comment { color: #788797; } +pre.rust .doccomment { color: #a1ac88; } + +nav.main .current { + border-top-color: #5c6773; + border-bottom-color: #5c6773; +} +nav.main .separator { + border: 1px solid #5c6773; +} +a { + color: #39AFD7; +} + +.sidebar h2 a, +.sidebar h3 a { + color: white; +} +.search-results a { + color: #0096cf; +} +body.source .example-wrap pre.rust a { + background: #333; +} + +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { + color: #999; +} + +details.rustdoc-toggle > summary::before { + filter: invert(100%); +} + +#crate-search, .search-input { + background-color: #141920; + border-color: #424c57; +} + +#crate-search { + /* Without the `!important`, the border-color is ignored for `<select>`... + It cannot be in the group above because `.search-input` has a different border color on + hover. */ + border-color: #424c57 !important; +} + +.search-input { + color: #ffffff; +} + +.module-item .stab, +.import-item .stab { + color: #000; +} + +/* Created this empty rule to satisfy the theme checks. */ +.stab.empty-impl {} +.stab.must_implement {} + +.stab.unstable, +.stab.deprecated, +.stab.portability, +.stab.empty-impl, +.stab.must_implement { + color: #c5c5c5; + background: #314559 !important; + border-style: none !important; + border-radius: 4px; + padding: 3px 6px 3px 6px; +} + +.stab.portability > code { + color: #e6e1cf; + background: none; +} + +.rightside, +.out-of-band { + color: grey; +} + +.result-name .primitive > i, .result-name .keyword > i { + color: #788797; +} + +.line-numbers :target { background-color: transparent; } + +/* Code highlighting */ +pre.rust .number, pre.rust .string { color: #b8cc52; } +pre.rust .kw, pre.rust .kw-2, pre.rust .prelude-ty, +pre.rust .bool-val, pre.rust .prelude-val, +pre.rust .op, pre.rust .lifetime { color: #ff7733; } +pre.rust .macro, pre.rust .macro-nonterminal { color: #a37acc; } +pre.rust .question-mark { + color: #ff9011; +} +pre.rust .self { + color: #36a3d9; + font-style: italic; +} +pre.rust .attribute { + color: #e6e1cf; +} +pre.rust .attribute .ident, pre.rust .attribute .op { + color: #e6e1cf; +} + +.example-wrap > pre.line-number { + color: #5c67736e; + border: none; +} + +a.test-arrow { + font-size: 100%; + color: #788797; + border-radius: 4px; + background-color: rgba(57, 175, 215, 0.09); +} + +a.test-arrow:hover { + background-color: rgba(57, 175, 215, 0.368); + color: #c5c5c5; +} + +.toggle-label, +.code-attribute { + color: #999; +} + +:target { + background: rgba(255, 236, 164, 0.06); + border-right: 3px solid rgba(255, 180, 76, 0.85); +} + +pre.compile_fail { + border-left: 2px solid rgba(255,0,0,.4); +} + +pre.compile_fail:hover, .information:hover + pre.compile_fail { + border-left: 2px solid #f00; +} + +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.4); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + +pre.ignore { + border-left: 2px solid rgba(255,142,0,.6); +} + +pre.ignore:hover, .information:hover + pre.ignore { + border-left: 2px solid #ff9200; +} + +.tooltip.compile_fail { + color: rgba(255,0,0,.5); +} + +.information > .compile_fail:hover { + color: #f00; +} + +.tooltip.should_panic { + color: rgba(255,0,0,.5); +} + +.information > .should_panic:hover { + color: #f00; +} + +.tooltip.ignore { + color: rgba(255,142,0,.6); +} + +.information > .ignore:hover { + color: #ff9200; +} + +.search-failed a { + color: #39AFD7; +} + +.tooltip::after { + background-color: #314559; + color: #c5c5c5; + border: 1px solid #5c6773; +} + +.tooltip::before { + border-color: transparent #314559 transparent transparent; +} + +.notable-traits-tooltiptext { + background-color: #314559; + border-color: #5c6773; +} + +.notable-traits-tooltiptext .notable { + border-bottom-color: #5c6773; +} + +#titles > button.selected { + background-color: #141920 !important; + border-bottom: 1px solid #ffb44c !important; + border-top: none; +} + +#titles > button:not(.selected) { + background-color: transparent !important; + border: none; +} + +#titles > button:hover { + border-bottom: 1px solid rgba(242, 151, 24, 0.3); +} + +#titles > button > div.count { + color: #888; +} + +/* rules that this theme does not need to set, here to satisfy the rule checker */ +/* note that a lot of these are partially set in some way (meaning they are set +individually rather than as a group) */ +/* FIXME: these rules should be at the bottom of the file but currently must be +above the `@media (max-width: 700px)` rules due to a bug in the css checker */ +/* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */ +.search-input:focus {} +.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive, +.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {} +.content span.struct,.content a.struct,.block a.current.struct {} +#titles>button:hover,#titles>button.selected {} +.content span.typedef,.content a.typedef,.block a.current.typedef {} +.content span.union,.content a.union,.block a.current.union {} +pre.rust .lifetime {} +.stab.unstable {} +h2, +h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {} +.content span.enum,.content a.enum,.block a.current.enum {} +.content span.constant,.content a.constant,.block a.current.constant,.content span.static, +.content a.static, .block a.current.static {} +.content span.keyword,.content a.keyword,.block a.current.keyword {} +pre.rust .comment {} +.content span.traitalias,.content a.traitalias,.block a.current.traitalias {} +.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method, +.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod, +.content .fnname {} +pre.rust .kw {} +pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute, +pre.rust .attribute .ident {} +.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {} +pre.rust .doccomment {} +.stab.deprecated {} +.content a.attr,.content a.derive,.content a.macro {} +.stab.portability {} +.content span.primitive,.content a.primitive,.block a.current.primitive {} +.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod {} +pre.rust .kw-2,pre.rust .prelude-ty {} +.content span.trait,.content a.trait,.block a.current.trait {} + +.search-results a:focus span {} +a.result-trait:focus {} +a.result-traitalias:focus {} +a.result-mod:focus, +a.result-externcrate:focus {} +a.result-mod:focus {} +a.result-externcrate:focus {} +a.result-enum:focus {} +a.result-struct:focus {} +a.result-union:focus {} +a.result-fn:focus, +a.result-method:focus, +a.result-tymethod:focus {} +a.result-type:focus {} +a.result-associatedtype:focus {} +a.result-foreigntype:focus {} +a.result-attr:focus, +a.result-derive:focus, +a.result-macro:focus {} +a.result-constant:focus, +a.result-static:focus {} +a.result-primitive:focus {} +a.result-keyword:focus {} + +.sidebar a.current.enum {} +.sidebar a.current.struct {} +.sidebar a.current.foreigntype {} +.sidebar a.current.attr, +.sidebar a.current.derive, +.sidebar a.current.macro {} +.sidebar a.current.union {} +.sidebar a.current.constant +.sidebar a.current.static {} +.sidebar a.current.primitive {} +.sidebar a.current.externcrate +.sidebar a.current.mod {} +.sidebar a.current.trait {} +.sidebar a.current.traitalias {} +.sidebar a.current.fn, +.sidebar a.current.method, +.sidebar a.current.tymethod {} +.sidebar a.current.keyword {} + +@media (max-width: 700px) { + .sidebar-elems { + border-right-color: #5c6773; + } +} + +kbd { + color: #c5c5c5; + background-color: #314559; + border-color: #5c6773; + border-bottom-color: #5c6773; + box-shadow: inset 0 -1px 0 #5c6773; +} + +#settings-menu > a, #help-button > button { + border-color: #5c6773; + background-color: #0f1419; + color: #fff; +} + +#settings-menu > a img { + filter: invert(100); +} + +.popover, .popover::before, +#help-button span.top, #help-button span.bottom { + border-color: #5c6773; +} + +#copy-path { + color: #fff; +} +#copy-path > img { + filter: invert(70%); +} +#copy-path:hover > img { + filter: invert(100%); +} + +#settings-menu > a:hover, #settings-menu > a:focus, +#help-button > button:hover, #help-button > button:focus { + border-color: #e0e0e0; +} + +#theme-choices { + border-color: #5c6773; + background-color: #0f1419; +} + +#theme-choices > button:not(:first-child) { + border-top-color: #5c6773; +} + +#theme-choices > button:hover, #theme-choices > button:focus { + background-color: rgba(110, 110, 110, 0.33); +} + +.search-results .result-name span.alias { + color: #c5c5c5; +} +.search-results .result-name span.grey { + color: #999; +} + +#source-sidebar > .title { + color: #fff; + border-bottom-color: #5c6773; +} +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { + background-color: #14191f; + color: #ffb44c; +} +#source-sidebar div.files > a.selected { + background-color: #14191f; + color: #ffb44c; +} + +.scraped-example-list .scrape-help { + border-color: #aaa; + color: #eee; +} +.scraped-example-list .scrape-help:hover { + border-color: white; + color: white; +} +.more-examples-toggle summary, .more-examples-toggle .hide-more { + color: #999; +} +.scraped-example .example-wrap .rust span.highlight { + background: rgb(91, 59, 1); +} +.scraped-example .example-wrap .rust span.highlight.focus { + background: rgb(124, 75, 15); +} +.scraped-example:not(.expanded) .code-wrapper:before { + background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); +} +.scraped-example:not(.expanded) .code-wrapper:after { + background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); +} +.toggle-line-inner { + background: #999; +} +.toggle-line:hover .toggle-line-inner { + background: #c5c5c5; +} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css new file mode 100644 index 000000000..a550eb1c1 --- /dev/null +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -0,0 +1,409 @@ +:root { + --main-background-color: #353535; + --main-color: #ddd; + --settings-input-color: #2196f3; + --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; +} + +.slider { + background-color: #ccc; +} +.slider:before { + background-color: white; +} +input:focus + .slider { + box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); +} + +.in-band { + background-color: #353535; +} + +.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) +} + +.sidebar .current, +.sidebar a:hover { + background: #444; +} + +.line-numbers span { color: #3B91E2; } +.line-numbers .line-highlighted { + background-color: #0a042f !important; +} + +.docblock table td, .docblock table th { + border-color: #ddd; +} + +.search-results a:hover { + background-color: #777; +} + +.search-results a:focus { + color: #eee !important; + background-color: #616161; +} +.search-results a:focus span { color: #eee !important; } +a.result-trait:focus { background-color: #013191; } +a.result-traitalias:focus { background-color: #013191; } +a.result-mod:focus, +a.result-externcrate:focus { background-color: #884719; } +a.result-enum:focus { background-color: #194e9f; } +a.result-struct:focus { background-color: #194e9f; } +a.result-union:focus { background-color: #194e9f; } +a.result-fn:focus, +a.result-method:focus, +a.result-tymethod:focus { background-color: #4950ed; } +a.result-type:focus { background-color: #194e9f; } +a.result-associatedtype:focus { background-color: #884719; } +a.result-foreigntype:focus { background-color: #194e9f; } +a.result-attr:focus, +a.result-derive:focus, +a.result-macro:focus { background-color: #217d1c; } +a.result-constant:focus, +a.result-static:focus { background-color: #884719; } +a.result-primitive:focus { background-color: #194e9f; } +a.result-keyword:focus { background-color: #884719; } + +.content .item-info::before { color: #ccc; } + +.content span.enum, .content a.enum, .block a.current.enum { color: #2dbfb8; } +.content span.struct, .content a.struct, .block a.current.struct { color: #2dbfb8; } +.content span.type, .content a.type, .block a.current.type { color: #2dbfb8; } +.content span.associatedtype, +.content a.associatedtype, +.block a.current.associatedtype { color: #D2991D; } +.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #2dbfb8; } +.content span.attr, .content a.attr, .block a.current.attr, +.content span.derive, .content a.derive, .block a.current.derive, +.content span.macro, .content a.macro, .block a.current.macro { color: #09bd00; } +.content span.union, .content a.union, .block a.current.union { color: #2dbfb8; } +.content span.constant, .content a.constant, .block a.current.constant, +.content span.static, .content a.static, .block a.current.static { color: #D2991D; } +.content span.primitive, .content a.primitive, .block a.current.primitive { color: #2dbfb8; } +.content span.externcrate, +.content span.mod, .content a.mod, .block a.current.mod { color: #D2991D; } +.content span.trait, .content a.trait, .block a.current.trait { color: #b78cf2; } +.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #b78cf2; } +.content span.fn, .content a.fn, .block a.current.fn, +.content span.method, .content a.method, .block a.current.method, +.content span.tymethod, .content a.tymethod, .block a.current.tymethod, +.content .fnname{ color: #2BAB63; } +.content span.keyword, .content a.keyword, .block a.current.keyword { color: #D2991D; } + +.sidebar a { color: #fdbf35; } +.sidebar a.current.enum { color: #12ece2; } +.sidebar a.current.struct { color: #12ece2; } +.sidebar a.current.type { color: #12ece2; } +.sidebar a.current.associatedtype { color: #fdbf35; } +.sidebar a.current.foreigntype { color: #12ece2; } +.sidebar a.current.attr, +.sidebar a.current.derive, +.sidebar a.current.macro { color: #0be900; } +.sidebar a.current.union { color: #12ece2; } +.sidebar a.current.constant +.sidebar a.current.static { color: #fdbf35; } +.sidebar a.current.primitive { color: #12ece2; } +.sidebar a.current.externcrate +.sidebar a.current.mod { color: #fdbf35; } +.sidebar a.current.trait { color: #cca7ff; } +.sidebar a.current.traitalias { color: #cca7ff; } +.sidebar a.current.fn, +.sidebar a.current.method, +.sidebar a.current.tymethod { color: #32d479; } +.sidebar a.current.keyword { color: #fdbf35; } + +pre.rust .comment { color: #8d8d8b; } +pre.rust .doccomment { color: #8ca375; } + +nav.main .current { + border-top-color: #eee; + border-bottom-color: #eee; +} +nav.main .separator { + border-color: #eee; +} + +a { + color: #D2991D; +} + +body.source .example-wrap pre.rust a { + background: #333; +} + +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { + color: #999; +} + +details.rustdoc-toggle > summary::before { + filter: invert(100%); +} + +#crate-search, .search-input { + color: #111; + background-color: #f0f0f0; + border-color: #f0f0f0; +} + +#crate-search { + /* Without the `!important`, the border-color is ignored for `<select>`... + It cannot be in the group above because `.search-input` has a different border color on + hover. */ + border-color: #f0f0f0 !important; +} + +.search-input { + border-color: #e0e0e0; +} + +.search-input:focus { + border-color: #008dfd; +} + +.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; } +.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; } +.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; } +.stab.must_implement { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; } +.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; } +.stab.portability > code { background: none; } + +.rightside, +.out-of-band { + color: grey; +} + +.line-numbers :target { background-color: transparent; } + +/* Code highlighting */ +pre.rust .kw { color: #ab8ac1; } +pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; } +pre.rust .number, pre.rust .string { color: #83a300; } +pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, +pre.rust .attribute, pre.rust .attribute .ident { color: #ee6868; } +pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } +pre.rust .lifetime { color: #d97f26; } +pre.rust .question-mark { + color: #ff9011; +} + +.example-wrap > pre.line-number { + border-color: #4a4949; +} + +a.test-arrow { + color: #dedede; + background-color: rgba(78, 139, 202, 0.2); +} + +a.test-arrow:hover{ + background-color: #4e8bca; +} + +.toggle-label, +.code-attribute { + color: #999; +} + +:target { + background-color: #494a3d; + border-right: 3px solid #bb7410; +} + +pre.compile_fail { + border-left: 2px solid rgba(255,0,0,.8); +} + +pre.compile_fail:hover, .information:hover + pre.compile_fail { + border-left: 2px solid #f00; +} + +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.8); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + +pre.ignore { + border-left: 2px solid rgba(255,142,0,.6); +} + +pre.ignore:hover, .information:hover + pre.ignore { + border-left: 2px solid #ff9200; +} + +.tooltip.compile_fail { + color: rgba(255,0,0,.8); +} + +.information > .compile_fail:hover { + color: #f00; +} + +.tooltip.should_panic { + color: rgba(255,0,0,.8); +} + +.information > .should_panic:hover { + color: #f00; +} + +.tooltip.ignore { + color: rgba(255,142,0,.6); +} + +.information > .ignore:hover { + color: #ff9200; +} + +.search-failed a { + color: #0089ff; +} + +.tooltip::after { + background-color: #000; + color: #fff; + border-color: #000; +} + +.tooltip::before { + border-color: transparent black transparent transparent; +} + +.notable-traits-tooltiptext { + background-color: #111; + border-color: #777; +} + +.notable-traits-tooltiptext .notable { + border-bottom-color: #d2d2d2; +} + +#titles > button:not(.selected) { + background-color: #252525; + border-top-color: #252525; +} + +#titles > button:hover, #titles > button.selected { + border-top-color: #0089ff; + background-color: #353535; +} + +#titles > button > div.count { + color: #888; +} + +@media (max-width: 700px) { + .sidebar-elems { + border-right-color: #000; + } +} + +kbd { + color: #000; + background-color: #fafbfc; + border-color: #d1d5da; + border-bottom-color: #c6cbd1; + box-shadow: inset 0 -1px 0 #c6cbd1; +} + +#settings-menu > a, #help-button > button { + border-color: #e0e0e0; + background: #f0f0f0; + color: #000; +} + +#settings-menu > a:hover, #settings-menu > a:focus, +#help-button > button:hover, #help-button > button:focus { + border-color: #ffb900; +} + +.popover, .popover::before, +#help-button span.top, #help-button span.bottom { + border-color: #d2d2d2; +} + +#copy-path { + color: #999; +} +#copy-path > img { + filter: invert(50%); +} +#copy-path:hover > img { + filter: invert(65%); +} + +#theme-choices { + border-color: #e0e0e0; + background-color: #353535; +} + +#theme-choices > button:not(:first-child) { + border-top-color: #e0e0e0; +} + +#theme-choices > button:hover, #theme-choices > button:focus { + background-color: #4e4e4e; +} + +.search-results .result-name span.alias { + color: #fff; +} +.search-results .result-name span.grey { + color: #ccc; +} + +#source-sidebar > .title { + border-bottom-color: #ccc; +} +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { + background-color: #444; +} +#source-sidebar div.files > a.selected { + background-color: #333; +} + +.scraped-example-list .scrape-help { + border-color: #aaa; + color: #eee; +} +.scraped-example-list .scrape-help:hover { + border-color: white; + color: white; +} +.more-examples-toggle summary, .more-examples-toggle .hide-more { + color: #999; +} +.scraped-example .example-wrap .rust span.highlight { + background: rgb(91, 59, 1); +} +.scraped-example .example-wrap .rust span.highlight.focus { + background: rgb(124, 75, 15); +} +.scraped-example:not(.expanded) .code-wrapper:before { + background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); +} +.scraped-example:not(.expanded) .code-wrapper:after { + background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); +} +.toggle-line-inner { + background: #999; +} +.toggle-line:hover .toggle-line-inner { + background: #c5c5c5; +} diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css new file mode 100644 index 000000000..b751acff1 --- /dev/null +++ b/src/librustdoc/html/static/css/themes/light.css @@ -0,0 +1,395 @@ +:root { + --main-background-color: white; + --main-color: black; + --settings-input-color: #2196f3; + --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; +} + +.slider { + background-color: #ccc; +} +.slider:before { + background-color: white; +} +input:focus + .slider { + box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); +} + +.in-band { + background-color: white; +} + +.rust-logo { + /* This rule exists to force other themes to explicitly style the logo. + * Rustdoc has a custom linter for this purpose. + */ +} + +.sidebar .current, +.sidebar a:hover { + background-color: #fff; +} + +.line-numbers span { color: #c67e2d; } +.line-numbers .line-highlighted { + background-color: #FDFFD3 !important; +} + +.docblock table td, .docblock table th { + border-color: #ddd; +} + +.search-results a:hover { + background-color: #ddd; +} + +.search-results a:focus { + color: #000 !important; + background-color: #ccc; +} +.search-results a:focus span { color: #000 !important; } +a.result-trait:focus { background-color: #c7b6ff; } +a.result-traitalias:focus { background-color: #c7b6ff; } +a.result-mod:focus, +a.result-externcrate:focus { background-color: #afc6e4; } +a.result-enum:focus { background-color: #e7b1a0; } +a.result-struct:focus { background-color: #e7b1a0; } +a.result-union:focus { background-color: #e7b1a0; } +a.result-fn:focus, +a.result-method:focus, +a.result-tymethod:focus { background-color: #c6afb3; } +a.result-type:focus { background-color: #e7b1a0; } +a.result-associatedtype:focus { background-color: #afc6e4; } +a.result-foreigntype:focus { background-color: #e7b1a0; } +a.result-attr:focus, +a.result-derive:focus, +a.result-macro:focus { background-color: #8ce488; } +a.result-constant:focus, +a.result-static:focus { background-color: #afc6e4; } +a.result-primitive:focus { background-color: #e7b1a0; } +a.result-keyword:focus { background-color: #afc6e4; } + +.content .item-info::before { color: #ccc; } + +.content span.enum, .content a.enum, .block a.current.enum { color: #AD378A; } +.content span.struct, .content a.struct, .block a.current.struct { color: #AD378A; } +.content span.type, .content a.type, .block a.current.type { color: #AD378A; } +.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #3873AD; } +.content span.associatedtype, +.content a.associatedtype, +.block a.current.associatedtype { color: #3873AD; } +.content span.attr, .content a.attr, .block a.current.attr, +.content span.derive, .content a.derive, .block a.current.derive, +.content span.macro, .content a.macro, .block a.current.macro { color: #068000; } +.content span.union, .content a.union, .block a.current.union { color: #AD378A; } +.content span.constant, .content a.constant, .block a.current.constant, +.content span.static, .content a.static, .block a.current.static { color: #3873AD; } +.content span.primitive, .content a.primitive, .block a.current.primitive { color: #AD378A; } +.content span.externcrate, +.content span.mod, .content a.mod, .block a.current.mod { color: #3873AD; } +.content span.trait, .content a.trait, .block a.current.trait { color: #6E4FC9; } +.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #5137AD; } +.content span.fn, .content a.fn, .block a.current.fn, +.content span.method, .content a.method, .block a.current.method, +.content span.tymethod, .content a.tymethod, .block a.current.tymethod, +.content .fnname { color: #AD7C37; } +.content span.keyword, .content a.keyword, .block a.current.keyword { color: #3873AD; } + +.sidebar a { color: #356da4; } +.sidebar a.current.enum { color: #a63283; } +.sidebar a.current.struct { color: #a63283; } +.sidebar a.current.type { color: #a63283; } +.sidebar a.current.associatedtype { color: #356da4; } +.sidebar a.current.foreigntype { color: #356da4; } +.sidebar a.current.attr, +.sidebar a.current.derive, +.sidebar a.current.macro { color: #067901; } +.sidebar a.current.union { color: #a63283; } +.sidebar a.current.constant +.sidebar a.current.static { color: #356da4; } +.sidebar a.current.primitive { color: #a63283; } +.sidebar a.current.externcrate +.sidebar a.current.mod { color: #356da4; } +.sidebar a.current.trait { color: #6849c3; } +.sidebar a.current.traitalias { color: #4b349e; } +.sidebar a.current.fn, +.sidebar a.current.method, +.sidebar a.current.tymethod { color: #a67736; } +.sidebar a.current.keyword { color: #356da4; } + +nav.main .current { + border-top-color: #000; + border-bottom-color: #000; +} +nav.main .separator { + border: 1px solid #000; +} + +a { + color: #3873AD; +} + +body.source .example-wrap pre.rust a { + background: #eee; +} + +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { + color: #999; +} + +#crate-search, .search-input { + background-color: white; + border-color: #e0e0e0; +} + +#crate-search { + /* Without the `!important`, the border-color is ignored for `<select>`... + It cannot be in the group above because `.search-input` has a different border color on + hover. */ + border-color: #e0e0e0 !important; +} + +.search-input:focus { + border-color: #66afe9; +} + +.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; } +.stab.unstable { background: #FFF5D6; border-color: #FFC600; } +.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; } +.stab.must_implement { background: #F3DFFF; border-color: #b07bdb; } +.stab.portability { background: #F3DFFF; border-color: #b07bdb; } +.stab.portability > code { background: none; } + +.rightside, +.out-of-band { + color: grey; +} + +.line-numbers :target { background-color: transparent; } + +/* Code highlighting */ +pre.rust .kw { color: #8959A8; } +pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } +pre.rust .number, pre.rust .string { color: #718C00; } +pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, +pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } +pre.rust .comment { color: #8E908C; } +pre.rust .doccomment { color: #4D4D4C; } +pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } +pre.rust .lifetime { color: #B76514; } +pre.rust .question-mark { + color: #ff9011; +} + +.example-wrap > pre.line-number { + border-color: #c7c7c7; +} + +a.test-arrow { + color: #f5f5f5; + background-color: rgba(78, 139, 202, 0.2); +} + +a.test-arrow:hover{ + background-color: #4e8bca; +} + +.toggle-label, +.code-attribute { + color: #999; +} + +:target { + background: #FDFFD3; + border-right: 3px solid #AD7C37; +} + +pre.compile_fail { + border-left: 2px solid rgba(255,0,0,.5); +} + +pre.compile_fail:hover, .information:hover + pre.compile_fail { + border-left: 2px solid #f00; +} + +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.5); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + +pre.ignore { + border-left: 2px solid rgba(255,142,0,.6); +} + +pre.ignore:hover, .information:hover + pre.ignore { + border-left: 2px solid #ff9200; +} + +.tooltip.compile_fail { + color: rgba(255,0,0,.5); +} + +.information > .compile_fail:hover { + color: #f00; +} + +.tooltip.should_panic { + color: rgba(255,0,0,.5); +} + +.information > .should_panic:hover { + color: #f00; +} + +.tooltip.ignore { + color: rgba(255,142,0,.6); +} + +.information > .ignore:hover { + color: #ff9200; +} + +.search-failed a { + color: #3873AD; +} + +.tooltip::after { + background-color: #000; + color: #fff; +} + +.tooltip::before { + border-color: transparent black transparent transparent; +} + +.notable-traits-tooltiptext { + background-color: #eee; + border-color: #999; +} + +.notable-traits-tooltiptext .notable { + border-bottom-color: #DDDDDD; +} + +#titles > button:not(.selected) { + background-color: #e6e6e6; + border-top-color: #e6e6e6; +} + +#titles > button:hover, #titles > button.selected { + background-color: #ffffff; + border-top-color: #0089ff; +} + +#titles > button > div.count { + color: #888; +} + +@media (max-width: 700px) { + .sidebar-elems { + border-right-color: #000; + } +} + +kbd { + color: #000; + background-color: #fafbfc; + border-color: #d1d5da; + border-bottom-color: #c6cbd1; + box-shadow: inset 0 -1px 0 #c6cbd1; +} + +#settings-menu > a, #help-button > button { + border-color: #e0e0e0; + background-color: #fff; +} + +#settings-menu > a:hover, #settings-menu > a:focus, +#help-button > button:hover, #help-button > button:focus { + border-color: #717171; +} + +.popover, .popover::before, +#help-button span.top, #help-button span.bottom { + border-color: #DDDDDD; +} + +#copy-path { + color: #999; +} +#copy-path > img { + filter: invert(50%); +} +#copy-path:hover > img { + filter: invert(35%); +} + +#theme-choices { + border-color: #ccc; + background-color: #fff; +} + +#theme-choices > button:not(:first-child) { + border-top-color: #e0e0e0; +} + +#theme-choices > button:hover, #theme-choices > button:focus { + background-color: #eee; +} + +.search-results .result-name span.alias { + color: #000; +} +.search-results .result-name span.grey { + color: #999; +} + +#source-sidebar > .title { + border-bottom-color: #ccc; +} +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { + background-color: #E0E0E0; +} +#source-sidebar div.files > a.selected { + background-color: #fff; +} +.scraped-example-list .scrape-help { + border-color: #555; + color: #333; +} +.scraped-example-list .scrape-help:hover { + border-color: black; + color: black; +} +.more-examples-toggle summary, .more-examples-toggle .hide-more { + color: #999; +} +.scraped-example .example-wrap .rust span.highlight { + background: #fcffd6; +} +.scraped-example .example-wrap .rust span.highlight.focus { + background: #f6fdb0; +} +.scraped-example:not(.expanded) .code-wrapper:before { + background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); +} +.scraped-example:not(.expanded) .code-wrapper:after { + background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); +} +.toggle-line-inner { + background: #ccc; +} +.toggle-line:hover .toggle-line-inner { + background: #999; +} diff --git a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt new file mode 100644 index 000000000..ff9afab06 --- /dev/null +++ b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt @@ -0,0 +1,94 @@ +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/librustdoc/html/static/fonts/FiraSans-Medium.woff2 b/src/librustdoc/html/static/fonts/FiraSans-Medium.woff2 Binary files differnew file mode 100644 index 000000000..7a1e5fc54 --- /dev/null +++ b/src/librustdoc/html/static/fonts/FiraSans-Medium.woff2 diff --git a/src/librustdoc/html/static/fonts/FiraSans-Regular.woff2 b/src/librustdoc/html/static/fonts/FiraSans-Regular.woff2 Binary files differnew file mode 100644 index 000000000..e766e06cc --- /dev/null +++ b/src/librustdoc/html/static/fonts/FiraSans-Regular.woff2 diff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt new file mode 100644 index 000000000..0bf46682b --- /dev/null +++ b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt @@ -0,0 +1,99 @@ +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff2 b/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff2 Binary files differnew file mode 100644 index 000000000..1866ad4bc --- /dev/null +++ b/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff2 Binary files differnew file mode 100644 index 000000000..462c34efc --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt new file mode 100644 index 000000000..07542572e --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff2 Binary files differnew file mode 100644 index 000000000..10b558e0b --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff2 Binary files differnew file mode 100644 index 000000000..5ec64eef0 --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 Binary files differnew file mode 100644 index 000000000..db57d2145 --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 Binary files differnew file mode 100644 index 000000000..1cbc021a3 --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md new file mode 100644 index 000000000..68ea18924 --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md @@ -0,0 +1,93 @@ +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 Binary files differnew file mode 100644 index 000000000..2db73fe2b --- /dev/null +++ b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 diff --git a/src/librustdoc/html/static/images/clipboard.svg b/src/librustdoc/html/static/images/clipboard.svg new file mode 100644 index 000000000..8adbd9963 --- /dev/null +++ b/src/librustdoc/html/static/images/clipboard.svg @@ -0,0 +1 @@ +<svg width="24" height="25" viewBox="0 0 24 25" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg> diff --git a/src/librustdoc/html/static/images/down-arrow.svg b/src/librustdoc/html/static/images/down-arrow.svg new file mode 100644 index 000000000..35437e77a --- /dev/null +++ b/src/librustdoc/html/static/images/down-arrow.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="128" height="128" enable-background="new 0 0 128 128" version="1.1" viewBox="-30 -20 176 176" xml:space="preserve"><g><line x1="111" x2="64" y1="40.5" y2="87.499" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/><line x1="64" x2="17" y1="87.499" y2="40.5" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/></g></svg>
\ No newline at end of file diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png Binary files differnew file mode 100644 index 000000000..ea4b45cae --- /dev/null +++ b/src/librustdoc/html/static/images/favicon-16x16.png diff --git a/src/librustdoc/html/static/images/favicon-32x32.png b/src/librustdoc/html/static/images/favicon-32x32.png Binary files differnew file mode 100644 index 000000000..69b8613ce --- /dev/null +++ b/src/librustdoc/html/static/images/favicon-32x32.png diff --git a/src/librustdoc/html/static/images/favicon.svg b/src/librustdoc/html/static/images/favicon.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/src/librustdoc/html/static/images/favicon.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;"> +<defs> + <style type="text/css"><![CDATA[ + #logo { + fill-rule: nonzero; + } + #logo-teeth { + stroke: #000000; + stroke-width: 0.92px; + } + @media (prefers-color-scheme: dark) { + #logo { + fill: #FFFFFF; + fill-rule: nonzero; + } + #logo-teeth { + fill: #FFFFFF; + stroke: #FFFFFF; + stroke-width: 0.92px; + } + } + ]]></style> +</defs> +<path id="logo" d="M15.993,1.54c-7.972,0 -14.461,6.492 -14.461,14.462c0,7.969 6.492,14.461 14.461,14.461c7.97,0 14.462,-6.492 14.462,-14.461c0,-7.97 -6.492,-14.462 -14.462,-14.462Zm-0.021,1.285c0.511,0.013 0.924,0.439 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0.001 0.028,0.001Zm2.178,1.566c3.379,0.633 6.313,2.723 8.016,5.709l-1.123,2.533c-0.193,0.438 0.006,0.952 0.44,1.147l2.16,0.958c0.067,0.675 0.076,1.355 0.025,2.031l-1.202,0c-0.12,0 -0.169,0.08 -0.169,0.196l0,0.551c0,1.297 -0.731,1.582 -1.373,1.652c-0.612,0.07 -1.288,-0.257 -1.374,-0.63c-0.361,-2.029 -0.961,-2.46 -1.909,-3.21c1.178,-0.746 2.401,-1.85 2.401,-3.325c0,-1.594 -1.092,-2.597 -1.835,-3.09c-1.046,-0.688 -2.203,-0.826 -2.515,-0.826l-12.421,0c1.717,-1.918 4.02,-3.218 6.55,-3.696l1.466,1.536c0.33,0.346 0.878,0.361 1.223,0.028l1.64,-1.564Zm-13.522,7.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.951 0.951,-0.951c0.009,0 0.019,0 0.028,0Zm22.685,0.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0 0.028,0.001Zm-20.892,0.153l1.658,0l0,7.477l-3.347,0c-0.414,-1.452 -0.542,-2.97 -0.38,-4.47l2.05,-0.912c0.438,-0.195 0.637,-0.706 0.441,-1.144l-0.422,-0.951Zm6.92,0.079l3.949,0c0.205,0 1.441,0.236 1.441,1.163c0,0.768 -0.948,1.043 -1.728,1.043l-3.665,0l0.003,-2.206Zm0,5.373l3.026,0c0.275,0 1.477,0.079 1.86,1.615c0.119,0.471 0.385,2.007 0.566,2.499c0.18,0.551 0.911,1.652 1.691,1.652l4.938,0c-0.331,0.444 -0.693,0.863 -1.083,1.255l-2.01,-0.432c-0.468,-0.101 -0.93,0.199 -1.031,0.667l-0.477,2.228c-3.104,1.406 -6.672,1.389 -9.762,-0.046l-0.478,-2.228c-0.101,-0.468 -0.56,-0.767 -1.028,-0.667l-1.967,0.423c-0.365,-0.377 -0.704,-0.778 -1.016,-1.2l9.567,0c0.107,0 0.181,-0.018 0.181,-0.119l0,-3.384c0,-0.097 -0.074,-0.119 -0.181,-0.119l-2.799,0l0.003,-2.144Zm-4.415,7.749c0.512,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.952,-0.43 -0.952,-0.952c0,0 0,0 0,0c0,-0.522 0.43,-0.952 0.952,-0.952c0.009,0 0.018,0.001 0.027,0.001Zm14.089,0.043c0.511,0.015 0.924,0.439 0.923,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.009,0 0.018,0 0.028,0.001Z"/><path id="logo-teeth" d="M29.647,16.002c0,7.49 -6.163,13.653 -13.654,13.653c-7.49,0 -13.654,-6.163 -13.654,-13.653c0,-7.491 6.164,-13.654 13.654,-13.654c7.491,0 13.654,6.163 13.654,13.654Zm-0.257,-1.319l2.13,1.319l-2.13,1.318l1.83,1.71l-2.344,0.878l1.463,2.035l-2.475,0.404l1.04,2.282l-2.506,-0.089l0.575,2.442l-2.441,-0.576l0.089,2.506l-2.283,-1.04l-0.403,2.475l-2.035,-1.462l-0.878,2.343l-1.71,-1.829l-1.319,2.129l-1.318,-2.129l-1.71,1.829l-0.878,-2.343l-2.035,1.462l-0.404,-2.475l-2.282,1.04l0.089,-2.506l-2.442,0.576l0.575,-2.442l-2.505,0.089l1.04,-2.282l-2.475,-0.404l1.462,-2.035l-2.343,-0.878l1.829,-1.71l-2.129,-1.318l2.129,-1.319l-1.829,-1.71l2.343,-0.878l-1.462,-2.035l2.475,-0.404l-1.04,-2.282l2.505,0.089l-0.575,-2.441l2.442,0.575l-0.089,-2.506l2.282,1.04l0.404,-2.475l2.035,1.463l0.878,-2.344l1.71,1.83l1.318,-2.13l1.319,2.13l1.71,-1.83l0.878,2.344l2.035,-1.463l0.403,2.475l2.283,-1.04l-0.089,2.506l2.441,-0.575l-0.575,2.441l2.506,-0.089l-1.04,2.282l2.475,0.404l-1.463,2.035l2.344,0.878l-1.83,1.71Z"/></svg> diff --git a/src/librustdoc/html/static/images/rust-logo.svg b/src/librustdoc/html/static/images/rust-logo.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/src/librustdoc/html/static/images/rust-logo.svg @@ -0,0 +1,61 @@ +<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="logo" transform="translate(53, 53)"> + <path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d=" + M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z + M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40 + V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35 + V -15 H -25 V 11 H -40 Z" /> + <g id="gear" mask="url(#holes)"> + <circle r="43" fill="none" stroke="black" stroke-width="9" /> + <g id="cogs"> + <polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3" /> + <use xlink:href="#cog" transform="rotate(11.25)" /> + <use xlink:href="#cog" transform="rotate(22.50)" /> + <use xlink:href="#cog" transform="rotate(33.75)" /> + <use xlink:href="#cog" transform="rotate(45.00)" /> + <use xlink:href="#cog" transform="rotate(56.25)" /> + <use xlink:href="#cog" transform="rotate(67.50)" /> + <use xlink:href="#cog" transform="rotate(78.75)" /> + <use xlink:href="#cog" transform="rotate(90.00)" /> + <use xlink:href="#cog" transform="rotate(101.25)" /> + <use xlink:href="#cog" transform="rotate(112.50)" /> + <use xlink:href="#cog" transform="rotate(123.75)" /> + <use xlink:href="#cog" transform="rotate(135.00)" /> + <use xlink:href="#cog" transform="rotate(146.25)" /> + <use xlink:href="#cog" transform="rotate(157.50)" /> + <use xlink:href="#cog" transform="rotate(168.75)" /> + <use xlink:href="#cog" transform="rotate(180.00)" /> + <use xlink:href="#cog" transform="rotate(191.25)" /> + <use xlink:href="#cog" transform="rotate(202.50)" /> + <use xlink:href="#cog" transform="rotate(213.75)" /> + <use xlink:href="#cog" transform="rotate(225.00)" /> + <use xlink:href="#cog" transform="rotate(236.25)" /> + <use xlink:href="#cog" transform="rotate(247.50)" /> + <use xlink:href="#cog" transform="rotate(258.75)" /> + <use xlink:href="#cog" transform="rotate(270.00)" /> + <use xlink:href="#cog" transform="rotate(281.25)" /> + <use xlink:href="#cog" transform="rotate(292.50)" /> + <use xlink:href="#cog" transform="rotate(303.75)" /> + <use xlink:href="#cog" transform="rotate(315.00)" /> + <use xlink:href="#cog" transform="rotate(326.25)" /> + <use xlink:href="#cog" transform="rotate(337.50)" /> + <use xlink:href="#cog" transform="rotate(348.75)" /> + </g> + <g id="mounts"> + <polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42" /> + <use xlink:href="#mount" transform="rotate(72)" /> + <use xlink:href="#mount" transform="rotate(144)" /> + <use xlink:href="#mount" transform="rotate(216)" /> + <use xlink:href="#mount" transform="rotate(288)" /> + </g> + </g> + <mask id="holes"> + <rect x="-60" y="-60" width="120" height="120" fill="white"/> + <circle id="hole" cy="-40" r="3" /> + <use xlink:href="#hole" transform="rotate(72)" /> + <use xlink:href="#hole" transform="rotate(144)" /> + <use xlink:href="#hole" transform="rotate(216)" /> + <use xlink:href="#hole" transform="rotate(288)" /> + </mask> +</g> +</svg> diff --git a/src/librustdoc/html/static/images/toggle-minus.svg b/src/librustdoc/html/static/images/toggle-minus.svg new file mode 100644 index 000000000..73154788a --- /dev/null +++ b/src/librustdoc/html/static/images/toggle-minus.svg @@ -0,0 +1 @@ +<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7"/></svg>
\ No newline at end of file diff --git a/src/librustdoc/html/static/images/toggle-plus.svg b/src/librustdoc/html/static/images/toggle-plus.svg new file mode 100644 index 000000000..08b17033e --- /dev/null +++ b/src/librustdoc/html/static/images/toggle-plus.svg @@ -0,0 +1 @@ +<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7M8.5 12V8.625v0V5"/></svg>
\ No newline at end of file diff --git a/src/librustdoc/html/static/images/wheel.svg b/src/librustdoc/html/static/images/wheel.svg new file mode 100644 index 000000000..01da3b24c --- /dev/null +++ b/src/librustdoc/html/static/images/wheel.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Capa_1" width="27.434" height="29.5" enable-background="new 0 0 27.434 29.5" version="1.1" viewBox="0 0 27.434 29.5" xml:space="preserve"><g><path d="M27.315,18.389c-0.165-0.604-0.509-1.113-0.981-1.459c-0.042-0.144-0.083-0.429-0.015-0.761l0.037-0.177v-0.182V14.8 c0-1.247-0.006-1.277-0.048-1.472c-0.076-0.354-0.035-0.653,0.007-0.803c0.477-0.346,0.828-0.861,0.996-1.476 c0.261-0.956,0.076-2.091-0.508-3.114l-0.591-1.032c-0.746-1.307-1.965-2.119-3.182-2.119c-0.378,0-0.75,0.081-1.085,0.235 c-0.198-0.025-0.554-0.15-0.855-0.389l-0.103-0.082l-0.114-0.065l-1.857-1.067L18.92,3.36l-0.105-0.044 c-0.376-0.154-0.658-0.41-0.768-0.556C17.918,1.172,16.349,0,14.296,0H13.14c-2.043,0-3.608,1.154-3.749,2.721 C9.277,2.862,8.999,3.104,8.633,3.25l-0.1,0.039L8.439,3.341L6.495,4.406L6.363,4.479L6.245,4.573 C5.936,4.82,5.596,4.944,5.416,4.977c-0.314-0.139-0.66-0.21-1.011-0.21c-1.198,0-2.411,0.819-3.165,2.139L0.65,7.938 c-0.412,0.72-0.642,1.521-0.644,2.258c-0.003,0.952,0.362,1.756,1.013,2.256c0.034,0.155,0.061,0.448-0.016,0.786 c-0.038,0.168-0.062,0.28-0.062,1.563c0,1.148,0,1.148,0.015,1.262l0.009,0.073l0.017,0.073c0.073,0.346,0.045,0.643,0.011,0.802 C0.348,17.512-0.01,18.314,0,19.268c0.008,0.729,0.238,1.523,0.648,2.242l0.589,1.031c0.761,1.331,1.967,2.159,3.15,2.159 c0.324,0,0.645-0.064,0.938-0.187c0.167,0.038,0.492,0.156,0.813,0.416l0.11,0.088l0.124,0.07l2.045,1.156l0.102,0.057l0.107,0.043 c0.364,0.147,0.646,0.381,0.766,0.521c0.164,1.52,1.719,2.634,3.745,2.634h1.155c2.037,0,3.598-1.134,3.747-2.675 c0.117-0.145,0.401-0.393,0.774-0.549l0.111-0.047l0.105-0.062l1.96-1.159l0.105-0.062l0.097-0.075 c0.309-0.246,0.651-0.371,0.832-0.402c0.313,0.138,0.662,0.212,1.016,0.212c1.199,0,2.412-0.82,3.166-2.139l0.59-1.032 C27.387,20.48,27.575,19.342,27.315,18.389z M25.274,20.635l-0.59,1.032c-0.438,0.765-1.104,1.251-1.639,1.251 c-0.133,0-0.258-0.029-0.369-0.094c-0.15-0.086-0.346-0.127-0.566-0.127c-0.596,0-1.383,0.295-2.01,0.796l-1.96,1.157 c-1.016,0.425-1.846,1.291-1.846,1.929s-0.898,1.159-1.998,1.159H13.14c-1.1,0-1.998-0.514-1.998-1.141s-0.834-1.477-1.854-1.888 l-2.046-1.157c-0.636-0.511-1.425-0.814-2.006-0.814c-0.202,0-0.379,0.037-0.516,0.115c-0.101,0.057-0.214,0.084-0.333,0.084 c-0.518,0-1.179-0.498-1.62-1.271l-0.591-1.032c-0.545-0.954-0.556-1.983-0.024-2.286c0.532-0.305,0.78-1.432,0.551-2.506 c0,0,0-0.003,0-1.042c0-1.088,0.021-1.18,0.021-1.18c0.238-1.072-0.01-2.203-0.552-2.513C1.631,10.8,1.634,9.765,2.18,8.812 L2.769,7.78c0.438-0.766,1.103-1.251,1.636-1.251c0.131,0,0.255,0.029,0.365,0.092C4.92,6.707,5.114,6.747,5.334,6.747 c0.596,0,1.38-0.296,2.007-0.795l1.944-1.065c1.021-0.407,1.856-1.277,1.856-1.933c0-0.656,0.898-1.192,1.998-1.192h1.156V1.761 c1.1,0,1.998,0.545,1.998,1.211c0,0.667,0.832,1.554,1.849,1.973L20,6.013c0.618,0.489,1.401,0.775,2.012,0.775 c0.24,0,0.454-0.045,0.62-0.139c0.122-0.069,0.259-0.102,0.403-0.102c0.551,0,1.221,0.476,1.653,1.231l0.59,1.032 c0.544,0.953,0.518,2.004-0.062,2.334c-0.577,0.331-0.859,1.48-0.627,2.554c0,0,0.01,0.042,0.01,1.103c0,1.012,0,1.012,0,1.012 c-0.218,1.049,0.068,2.174,0.636,2.498C25.802,18.635,25.819,19.68,25.274,20.635z"/><path d="M13.61,7.611c-3.913,0-7.084,3.173-7.084,7.085c0,3.914,3.171,7.085,7.084,7.085s7.085-3.172,7.085-7.085 C20.695,10.784,17.523,7.611,13.61,7.611z M13.61,20.02c-2.936,0-5.323-2.388-5.323-5.323c0-2.935,2.388-5.323,5.323-5.323 s5.324,2.388,5.324,5.323C18.934,17.632,16.546,20.02,13.61,20.02z"/><path d="M13.682,9.908c-2.602,0-4.718,2.116-4.718,4.718c0,2.601,2.116,4.716,4.718,4.716c2.601,0,4.717-2.115,4.717-4.716 C18.399,12.024,16.283,9.908,13.682,9.908z M13.682,17.581c-1.633,0-2.956-1.323-2.956-2.955s1.323-2.956,2.956-2.956 c1.632,0,2.956,1.324,2.956,2.956S15.314,17.581,13.682,17.581z"/></g></svg>
\ No newline at end of file diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md new file mode 100644 index 000000000..1fd859ad7 --- /dev/null +++ b/src/librustdoc/html/static/js/README.md @@ -0,0 +1,15 @@ +# Rustdoc JS + +These JavaScript files are incorporated into the rustdoc binary at build time, +and are minified and written to the filesystem as part of the doc build process. + +We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler) +dialect of JSDoc to comment our code and annotate params and return types. +To run a check: + + ./x.py doc library/std + npm i -g google-closure-compiler + google-closure-compiler -W VERBOSE \ + build/<YOUR PLATFORM>/doc/{search-index*.js,crates*.js} \ + src/librustdoc/html/static/js/{search.js,main.js,storage.js} \ + --externs src/librustdoc/html/static/js/externs.js >/dev/null diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js new file mode 100644 index 000000000..ecbe15a59 --- /dev/null +++ b/src/librustdoc/html/static/js/externs.js @@ -0,0 +1,142 @@ +// This file contains type definitions that are processed by the Closure Compiler but are +// not put into the JavaScript we include as part of the documentation. It is used for +// type checking. See README.md in this directory for more info. + +/* eslint-disable */ +let searchState; +function initSearch(searchIndex){} + +/** + * @typedef {{ + * name: string, + * fullPath: Array<string>, + * pathWithoutLast: Array<string>, + * pathLast: string, + * generics: Array<QueryElement>, + * }} + */ +let QueryElement; + +/** + * @typedef {{ + * pos: number, + * totalElems: number, + * typeFilter: (null|string), + * userQuery: string, + * }} + */ +let ParserState; + +/** + * @typedef {{ + * original: string, + * userQuery: string, + * typeFilter: number, + * elems: Array<QueryElement>, + * args: Array<QueryElement>, + * returned: Array<QueryElement>, + * foundElems: number, + * }} + */ +let ParsedQuery; + +/** + * @typedef {{ + * crate: string, + * desc: string, + * id: number, + * name: string, + * normalizedName: string, + * parent: (Object|null|undefined), + * path: string, + * ty: (Number|null|number), + * type: (Array<?>|null) + * }} + */ +let Row; + +/** + * @typedef {{ + * in_args: Array<Object>, + * returned: Array<Object>, + * others: Array<Object>, + * query: ParsedQuery, + * }} + */ +let ResultsTable; + +/** + * @typedef {{ + * desc: string, + * displayPath: string, + * fullPath: string, + * href: string, + * id: number, + * lev: number, + * name: string, + * normalizedName: string, + * parent: (Object|undefined), + * path: string, + * ty: number, + * }} + */ +let Results; + +/** + * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * An input or output can be encoded as just a number if there is only one of them, AND + * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had + * a function with a single output, and that output had a single generic: + * + * fn something() -> Result<usize, usize> + * + * If output was allowed to be any RawFunctionType, it would look like this + * + * [[], [50, [3, 3]]] + * + * The problem is that the above output could be interpreted as either a type with ID 50 and two + * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second + * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing + * in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)` + * is used instead of `(RawFunctionType|Array<RawFunctionType>)`. + * + * @typedef {( + * 0 | + * [(number|Array<RawFunctionType>)] | + * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] + * )} + */ +let RawFunctionSearchType; + +/** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * @typedef {number | [number, Array<RawFunctionType>]} + */ +let RawFunctionType; + +/** + * @typedef {{ + * inputs: Array<FunctionType>, + * outputs: Array<FunctionType>, + * }} + */ +let FunctionSearchType; + +/** + * @typedef {{ + * name: (null|string), + * ty: (null|number), + * generics: Array<FunctionType>, + * }} + */ +let FunctionType; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js new file mode 100644 index 000000000..0702b2b0b --- /dev/null +++ b/src/librustdoc/html/static/js/main.js @@ -0,0 +1,974 @@ +// Local js definitions: +/* global addClass, getSettingValue, hasClass, searchState */ +/* global onEach, onEachLazy, removeClass */ + +"use strict"; + +// Get a value from the rustdoc-vars div, which is used to convey data from +// Rust to the JS. If there is no such element, return null. +function getVar(name) { + const el = document.getElementById("rustdoc-vars"); + if (el) { + return el.attributes["data-" + name].value; + } else { + return null; + } +} + +// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL +// for a resource under the root-path, with the resource-suffix. +function resourcePath(basename, extension) { + return getVar("root-path") + basename + getVar("resource-suffix") + extension; +} + +function hideMain() { + addClass(document.getElementById(MAIN_ID), "hidden"); +} + +function showMain() { + removeClass(document.getElementById(MAIN_ID), "hidden"); +} + +function elemIsInParent(elem, parent) { + while (elem && elem !== document.body) { + if (elem === parent) { + return true; + } + elem = elem.parentElement; + } + return false; +} + +function blurHandler(event, parentElem, hideCallback) { + if (!elemIsInParent(document.activeElement, parentElem) && + !elemIsInParent(event.relatedTarget, parentElem) + ) { + hideCallback(); + } +} + +(function() { + window.rootPath = getVar("root-path"); + 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.location"); + const locationTitle = document.querySelector(".sidebar h2.location"); + if (mobileLocationTitle && locationTitle) { + mobileLocationTitle.innerHTML = locationTitle.innerHTML; + } +} + +// Gets the human-readable string for the virtual-key code of the +// given KeyboardEvent, ev. +// +// This function is meant as a polyfill for KeyboardEvent#key, +// since it is not supported in IE 11 or Chrome for Android. We also test for +// KeyboardEvent#keyCode because the handleShortcut handler is +// also registered for the keydown event, because Blink doesn't fire +// keypress on hitting the Escape key. +// +// So I guess you could say things are getting pretty interoperable. +function getVirtualKey(ev) { + if ("key" in ev && typeof ev.key !== "undefined") { + return ev.key; + } + + const c = ev.charCode || ev.keyCode; + if (c === 27) { + return "Escape"; + } + return String.fromCharCode(c); +} + +const MAIN_ID = "main-content"; +const SETTINGS_BUTTON_ID = "settings-menu"; +const ALTERNATIVE_DISPLAY_ID = "alternative-display"; +const NOT_DISPLAYED_ID = "not-displayed"; +const HELP_BUTTON_ID = "help-button"; + +function getSettingsButton() { + return document.getElementById(SETTINGS_BUTTON_ID); +} + +function getHelpButton() { + return document.getElementById(HELP_BUTTON_ID); +} + +// Returns the current URL without any query parameter or hash. +function getNakedUrl() { + return window.location.href.split("?")[0].split("#")[0]; +} + +/** + * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` + * doesn't have a parent node. + * + * @param {HTMLElement} newNode + * @param {HTMLElement} referenceNode + */ +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +/** + * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already + * exist. + * + * More information about this in `switchDisplayedElement` documentation. + * + * @param {string} id + * @param {string} classes + */ +function getOrCreateSection(id, classes) { + let el = document.getElementById(id); + + if (!el) { + el = document.createElement("section"); + el.id = id; + el.className = classes; + insertAfter(el, document.getElementById(MAIN_ID)); + } + return el; +} + +/** + * Returns the `<section>` element which contains the displayed element. + * + * @return {HTMLElement} + */ +function getAlternativeDisplayElem() { + return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); +} + +/** + * Returns the `<section>` element which contains the not-displayed elements. + * + * @return {HTMLElement} + */ +function getNotDisplayedElem() { + return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); +} + +/** + * To nicely switch between displayed "extra" elements (such as search results or settings menu) + * and to alternate between the displayed and not displayed elements, we hold them in two different + * `<section>` elements. They work in pair: one holds the hidden elements while the other + * contains the displayed element (there can be only one at the same time!). So basically, we switch + * elements between the two `<section>` elements. + * + * @param {HTMLElement} elemToDisplay + */ +function switchDisplayedElement(elemToDisplay) { + const el = getAlternativeDisplayElem(); + + if (el.children.length > 0) { + getNotDisplayedElem().appendChild(el.firstElementChild); + } + if (elemToDisplay === null) { + addClass(el, "hidden"); + showMain(); + return; + } + el.appendChild(elemToDisplay); + hideMain(); + removeClass(el, "hidden"); +} + +function browserSupportsHistoryApi() { + return window.history && typeof window.history.pushState === "function"; +} + +// eslint-disable-next-line no-unused-vars +function loadCss(cssFileName) { + const link = document.createElement("link"); + link.href = resourcePath(cssFileName, ".css"); + link.type = "text/css"; + link.rel = "stylesheet"; + document.getElementsByTagName("head")[0].appendChild(link); +} + +(function() { + function loadScript(url) { + const script = document.createElement("script"); + script.src = url; + document.head.append(script); + } + + getSettingsButton().onclick = event => { + addClass(getSettingsButton(), "rotate"); + 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("settings"); + loadScript(resourcePath("settings", ".js")); + }; + + window.searchState = { + loadingText: "Loading search results...", + input: document.getElementsByClassName("search-input")[0], + outputElement: () => { + let el = document.getElementById("search"); + if (!el) { + el = document.createElement("section"); + el.id = "search"; + getNotDisplayedElem().appendChild(el); + } + return el; + }, + title: document.title, + titleBeforeSearch: document.title, + timeout: null, + // On the search screen, so you remain on the last tab you opened. + // + // 0 for "In Names" + // 1 for "In Parameters" + // 2 for "In Return Types" + currentTab: 0, + // tab and back preserves the element that was focused. + focusedByTab: [null, null, null], + clearInputTimeout: () => { + if (searchState.timeout !== null) { + clearTimeout(searchState.timeout); + searchState.timeout = null; + } + }, + isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, + // Sets the focus on the search bar at the top of the page + focus: () => { + searchState.input.focus(); + }, + // Removes the focus from the search bar. + defocus: () => { + searchState.input.blur(); + }, + showResults: search => { + if (search === null || typeof search === "undefined") { + search = searchState.outputElement(); + } + switchDisplayedElement(search); + searchState.mouseMovedAfterSearch = false; + document.title = searchState.title; + }, + hideResults: () => { + switchDisplayedElement(null); + document.title = searchState.titleBeforeSearch; + // We also remove the query parameter from the URL. + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + }, + getQueryStringParams: () => { + const params = {}; + window.location.search.substring(1).split("&"). + map(s => { + const pair = s.split("="); + params[decodeURIComponent(pair[0])] = + typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); + }); + return params; + }, + setup: () => { + const search_input = searchState.input; + if (!searchState.input) { + return; + } + let searchLoaded = false; + function loadSearch() { + if (!searchLoaded) { + searchLoaded = true; + loadScript(resourcePath("search", ".js")); + loadScript(resourcePath("search-index", ".js")); + } + } + + search_input.addEventListener("focus", () => { + search_input.origPlaceholder = search_input.placeholder; + search_input.placeholder = "Type your search here."; + loadSearch(); + }); + + if (search_input.value !== "") { + loadSearch(); + } + + const params = searchState.getQueryStringParams(); + if (params.search !== undefined) { + const search = searchState.outputElement(); + search.innerHTML = "<h3 class=\"search-loading\">" + + searchState.loadingText + "</h3>"; + searchState.showResults(search); + loadSearch(); + } + }, + }; + + function getPageId() { + if (window.location.hash) { + const tmp = window.location.hash.replace(/^#/, ""); + if (tmp.length > 0) { + return tmp; + } + } + return null; + } + + const toggleAllDocsId = "toggle-all-docs"; + let savedHash = ""; + + function handleHashes(ev) { + if (ev !== null && searchState.isDisplayed() && ev.newURL) { + // This block occurs when clicking on an element in the navbar while + // in a search. + switchDisplayedElement(null); + const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); + if (browserSupportsHistoryApi()) { + // `window.location.search`` contains all the query parameters, not just `search`. + history.replaceState(null, "", + getNakedUrl() + window.location.search + "#" + hash); + } + const elem = document.getElementById(hash); + if (elem) { + elem.scrollIntoView(); + } + } + // This part is used in case an element is not visible. + if (savedHash !== window.location.hash) { + savedHash = window.location.hash; + if (savedHash.length === 0) { + return; + } + expandSection(savedHash.slice(1)); // we remove the '#' + } + } + + function onHashChange(ev) { + // If we're in mobile mode, we should hide the sidebar in any case. + const sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + handleHashes(ev); + } + + function openParentDetails(elem) { + while (elem) { + if (elem.tagName === "DETAILS") { + elem.open = true; + } + elem = elem.parentNode; + } + } + + function expandSection(id) { + openParentDetails(document.getElementById(id)); + } + + function handleEscape(ev) { + searchState.clearInputTimeout(); + switchDisplayedElement(null); + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + ev.preventDefault(); + searchState.defocus(); + window.hidePopoverMenus(); + } + + function handleShortcut(ev) { + // Don't interfere with browser shortcuts + const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; + if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { + return; + } + + if (document.activeElement.tagName === "INPUT" && + document.activeElement.type !== "checkbox") { + switch (getVirtualKey(ev)) { + case "Escape": + handleEscape(ev); + break; + } + } else { + switch (getVirtualKey(ev)) { + case "Escape": + handleEscape(ev); + break; + + case "s": + case "S": + ev.preventDefault(); + searchState.focus(); + break; + + case "+": + case "-": + ev.preventDefault(); + toggleAllDocs(); + break; + + case "?": + showHelp(); + break; + + default: + break; + } + } + } + + document.addEventListener("keypress", handleShortcut); + document.addEventListener("keydown", handleShortcut); + + function addSidebarItems() { + if (!window.SIDEBAR_ITEMS) { + return; + } + const sidebar = document.getElementsByClassName("sidebar-elems")[0]; + + /** + * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items. + * + * @param {string} shortty - A short type name, like "primitive", "mod", or "macro" + * @param {string} id - The HTML id of the corresponding section on the module page. + * @param {string} longty - A long, capitalized, plural name, like "Primitive Types", + * "Modules", or "Macros". + */ + function block(shortty, id, longty) { + const filtered = window.SIDEBAR_ITEMS[shortty]; + if (!filtered) { + return; + } + + const div = document.createElement("div"); + div.className = "block " + shortty; + const h3 = document.createElement("h3"); + h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`; + div.appendChild(h3); + const ul = document.createElement("ul"); + + for (const item of filtered) { + const name = item[0]; + const desc = item[1]; // can be null + + let klass = shortty; + let path; + if (shortty === "mod") { + path = name + "/index.html"; + } else { + path = shortty + "." + name + ".html"; + } + const current_page = document.location.href.split("/").pop(); + if (path === current_page) { + klass += " current"; + } + const link = document.createElement("a"); + link.href = path; + link.title = desc; + link.className = klass; + link.textContent = name; + const li = document.createElement("li"); + li.appendChild(link); + ul.appendChild(li); + } + div.appendChild(ul); + sidebar.appendChild(div); + } + + if (sidebar) { + block("primitive", "primitives", "Primitive Types"); + block("mod", "modules", "Modules"); + block("macro", "macros", "Macros"); + block("struct", "structs", "Structs"); + block("enum", "enums", "Enums"); + block("union", "unions", "Unions"); + block("constant", "constants", "Constants"); + block("static", "static", "Statics"); + block("trait", "traits", "Traits"); + block("fn", "functions", "Functions"); + block("type", "types", "Type Definitions"); + block("foreigntype", "foreign-types", "Foreign Types"); + block("keyword", "keywords", "Keywords"); + block("traitalias", "trait-aliases", "Trait Aliases"); + } + } + + window.register_implementors = imp => { + const implementors = document.getElementById("implementors-list"); + const synthetic_implementors = document.getElementById("synthetic-implementors-list"); + const inlined_types = new Set(); + + if (synthetic_implementors) { + // This `inlined_types` variable is used to avoid having the same implementation + // showing up twice. For example "String" in the "Sync" doc page. + // + // By the way, this is only used by and useful for traits implemented automatically + // (like "Send" and "Sync"). + onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { + const aliases = el.getAttribute("data-aliases"); + if (!aliases) { + return; + } + aliases.split(",").forEach(alias => { + inlined_types.add(alias); + }); + }); + } + + let currentNbImpls = implementors.getElementsByClassName("impl").length; + const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent; + const baseIdName = "impl-" + traitName + "-"; + const libs = Object.getOwnPropertyNames(imp); + // We don't want to include impls from this JS file, when the HTML already has them. + // The current crate should always be ignored. Other crates that should also be + // ignored are included in the attribute `data-ignore-extern-crates`. + const ignoreExternCrates = document + .querySelector("script[data-ignore-extern-crates]") + .getAttribute("data-ignore-extern-crates"); + for (const lib of libs) { + if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) { + continue; + } + const structs = imp[lib]; + + struct_loop: + for (const struct of structs) { + const list = struct.synthetic ? synthetic_implementors : implementors; + + if (struct.synthetic) { + for (const struct_type of struct.types) { + if (inlined_types.has(struct_type)) { + continue struct_loop; + } + inlined_types.add(struct_type); + } + } + + const code = document.createElement("h3"); + code.innerHTML = struct.text; + addClass(code, "code-header"); + addClass(code, "in-band"); + + onEachLazy(code.getElementsByTagName("a"), elem => { + const href = elem.getAttribute("href"); + + if (href && href.indexOf("http") !== 0) { + elem.setAttribute("href", window.rootPath + href); + } + }); + + const currentId = baseIdName + currentNbImpls; + const anchor = document.createElement("a"); + anchor.href = "#" + currentId; + addClass(anchor, "anchor"); + + const display = document.createElement("div"); + display.id = currentId; + addClass(display, "impl"); + display.appendChild(anchor); + display.appendChild(code); + list.appendChild(display); + currentNbImpls += 1; + } + } + }; + if (window.pending_implementors) { + window.register_implementors(window.pending_implementors); + } + + function addSidebarCrates() { + if (!window.ALL_CRATES) { + return; + } + const sidebarElems = document.getElementsByClassName("sidebar-elems")[0]; + if (!sidebarElems) { + return; + } + // Draw a convenient sidebar of known crates if we have a listing + const div = document.createElement("div"); + div.className = "block crate"; + div.innerHTML = "<h3>Crates</h3>"; + const ul = document.createElement("ul"); + div.appendChild(ul); + + for (const crate of window.ALL_CRATES) { + let klass = "crate"; + if (window.rootPath !== "./" && crate === window.currentCrate) { + klass += " current"; + } + const link = document.createElement("a"); + link.href = window.rootPath + crate + "/index.html"; + link.className = klass; + link.textContent = crate; + + const li = document.createElement("li"); + li.appendChild(link); + ul.appendChild(li); + } + sidebarElems.appendChild(div); + } + + + function labelForToggleButton(sectionIsCollapsed) { + if (sectionIsCollapsed) { + // button will expand the section + return "+"; + } + // button will collapse the section + // note that this text is also set in the HTML template in ../render/mod.rs + return "\u2212"; // "\u2212" is "−" minus sign + } + + function toggleAllDocs() { + const innerToggle = document.getElementById(toggleAllDocsId); + if (!innerToggle) { + return; + } + let sectionIsCollapsed = false; + if (hasClass(innerToggle, "will-expand")) { + removeClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (!hasClass(e, "type-contents-toggle")) { + e.open = true; + } + }); + innerToggle.title = "collapse all docs"; + } else { + addClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (e.parentNode.id !== "implementations-list" || + (!hasClass(e, "implementors-toggle") && + !hasClass(e, "type-contents-toggle")) + ) { + e.open = false; + } + }); + sectionIsCollapsed = true; + innerToggle.title = "expand all docs"; + } + innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed); + } + + (function() { + const toggles = document.getElementById(toggleAllDocsId); + if (toggles) { + toggles.onclick = toggleAllDocs; + } + + const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; + const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; + const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; + + function setImplementorsTogglesOpen(id, open) { + const list = document.getElementById(id); + if (list !== null) { + onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { + e.open = open; + }); + } + } + + if (hideImplementations) { + setImplementorsTogglesOpen("trait-implementations-list", false); + setImplementorsTogglesOpen("blanket-implementations-list", false); + } + + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { + e.open = true; + } + if (hideMethodDocs && hasClass(e, "method-toggle")) { + e.open = false; + } + + }); + + const pageId = getPageId(); + if (pageId !== null) { + expandSection(pageId); + } + }()); + + (function() { + // To avoid checking on "rustdoc-line-numbers" value on every loop... + let lineNumbersFunc = () => {}; + if (getSettingValue("line-numbers") === "true") { + lineNumbersFunc = x => { + const count = x.textContent.split("\n").length; + const elems = []; + for (let i = 0; i < count; ++i) { + elems.push(i + 1); + } + const node = document.createElement("pre"); + addClass(node, "line-number"); + node.innerHTML = elems.join("\n"); + x.parentNode.insertBefore(node, x); + }; + } + onEachLazy(document.getElementsByClassName("rust-example-rendered"), e => { + if (hasClass(e, "compile_fail")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } else if (hasClass(e, "ignore")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } + lineNumbersFunc(e); + }); + }()); + + function hideSidebar() { + const sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + } + + function handleClick(id, f) { + const elem = document.getElementById(id); + if (elem) { + elem.addEventListener("click", f); + } + } + handleClick(MAIN_ID, () => { + hideSidebar(); + }); + + onEachLazy(document.getElementsByTagName("a"), el => { + // For clicks on internal links (<A> tags with a hash property), we expand the section we're + // jumping to *before* jumping there. We can't do this in onHashChange, because it changes + // the height of the document so we wind up scrolled to the wrong place. + if (el.hash) { + el.addEventListener("click", () => { + expandSection(el.hash.slice(1)); + hideSidebar(); + }); + } + }); + + onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => { + el.addEventListener("click", e => { + if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { + e.preventDefault(); + } + }); + }); + + onEachLazy(document.getElementsByClassName("notable-traits"), e => { + e.onclick = function() { + this.getElementsByClassName("notable-traits-tooltiptext")[0] + .classList.toggle("force-tooltip"); + }; + }); + + const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; + if (sidebar_menu_toggle) { + sidebar_menu_toggle.addEventListener("click", () => { + const sidebar = document.getElementsByClassName("sidebar")[0]; + if (!hasClass(sidebar, "shown")) { + addClass(sidebar, "shown"); + } else { + removeClass(sidebar, "shown"); + } + }); + } + + function helpBlurHandler(event) { + blurHandler(event, getHelpButton(), window.hidePopoverMenus); + } + + function buildHelpMenu() { + const book_info = document.createElement("span"); + book_info.className = "top"; + book_info.innerHTML = "You can find more information in \ + <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>."; + + const shortcuts = [ + ["?", "Show this help dialog"], + ["S", "Focus the search field"], + ["↑", "Move up in search results"], + ["↓", "Move down in search results"], + ["← / →", "Switch result tab (when results focused)"], + ["⏎", "Go to active search result"], + ["+", "Expand all sections"], + ["-", "Collapse all sections"], + ].map(x => "<dt>" + + x[0].split(" ") + .map((y, index) => ((index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " ")) + .join("") + "</dt><dd>" + x[1] + "</dd>").join(""); + const div_shortcuts = document.createElement("div"); + addClass(div_shortcuts, "shortcuts"); + div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>"; + + const infos = [ + "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \ + restrict the search to a given item kind.", + "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \ + <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \ + and <code>const</code>.", + "Search functions by type signature (e.g., <code>vec -> usize</code> or \ + <code>-> vec</code>)", + "Search multiple things at once by splitting your query with comma (e.g., \ + <code>str,u8</code> or <code>String,struct:Vec,test</code>)", + "You can look for items with an exact name by putting double quotes around \ + your request: <code>\"string\"</code>", + "Look for items inside another one by searching for a path: <code>vec::Vec</code>", + ].map(x => "<p>" + x + "</p>").join(""); + const div_infos = document.createElement("div"); + addClass(div_infos, "infos"); + div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos; + + const rustdoc_version = document.createElement("span"); + rustdoc_version.className = "bottom"; + const rustdoc_version_code = document.createElement("code"); + rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version"); + rustdoc_version.appendChild(rustdoc_version_code); + + const container = document.createElement("div"); + container.className = "popover"; + container.style.display = "none"; + + const side_by_side = document.createElement("div"); + side_by_side.className = "side-by-side"; + side_by_side.appendChild(div_shortcuts); + side_by_side.appendChild(div_infos); + + container.appendChild(book_info); + container.appendChild(side_by_side); + container.appendChild(rustdoc_version); + + const help_button = getHelpButton(); + help_button.appendChild(container); + + container.onblur = helpBlurHandler; + container.onclick = event => { + event.preventDefault(); + }; + help_button.onblur = helpBlurHandler; + help_button.children[0].onblur = helpBlurHandler; + + return container; + } + + /** + * Hide all the popover menus. + */ + window.hidePopoverMenus = function() { + onEachLazy(document.querySelectorAll(".search-container .popover"), elem => { + elem.style.display = "none"; + }); + }; + + /** + * Returns the help menu element (not the button). + * + * @param {boolean} buildNeeded - If this argument is `false`, the help menu element won't be + * built if it doesn't exist. + * + * @return {HTMLElement} + */ + function getHelpMenu(buildNeeded) { + let menu = getHelpButton().querySelector(".popover"); + if (!menu && buildNeeded) { + menu = buildHelpMenu(); + } + return menu; + } + + /** + * Show the help popup menu. + */ + function showHelp() { + const menu = getHelpMenu(true); + if (menu.style.display === "none") { + window.hidePopoverMenus(); + menu.style.display = ""; + } + } + + document.querySelector(`#${HELP_BUTTON_ID} > button`).addEventListener("click", event => { + const target = event.target; + if (target.tagName !== "BUTTON" || target.parentElement.id !== HELP_BUTTON_ID) { + return; + } + const menu = getHelpMenu(true); + const shouldShowHelp = menu.style.display === "none"; + if (shouldShowHelp) { + showHelp(); + } else { + window.hidePopoverMenus(); + } + }); + + setMobileTopbar(); + addSidebarItems(); + addSidebarCrates(); + onHashChange(null); + window.addEventListener("hashchange", onHashChange); + searchState.setup(); +}()); + +(function() { + let reset_button_timeout = null; + + window.copy_path = but => { + const parent = but.parentElement; + const path = []; + + onEach(parent.childNodes, child => { + if (child.tagName === "A") { + path.push(child.textContent); + } + }); + + const el = document.createElement("textarea"); + el.value = path.join("::"); + el.setAttribute("readonly", ""); + // To not make it appear on the screen. + el.style.position = "absolute"; + el.style.left = "-9999px"; + + document.body.appendChild(el); + el.select(); + document.execCommand("copy"); + document.body.removeChild(el); + + // There is always one children, but multiple childNodes. + but.children[0].style.display = "none"; + + let tmp; + if (but.childNodes.length < 2) { + tmp = document.createTextNode("✓"); + but.appendChild(tmp); + } else { + onEachLazy(but.childNodes, e => { + if (e.nodeType === Node.TEXT_NODE) { + tmp = e; + return true; + } + }); + tmp.textContent = "✓"; + } + + if (reset_button_timeout !== null) { + window.clearTimeout(reset_button_timeout); + } + + function reset_button() { + tmp.textContent = ""; + reset_button_timeout = null; + but.children[0].style.display = ""; + } + + reset_button_timeout = window.setTimeout(reset_button, 1000); + }; +}()); diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js new file mode 100644 index 000000000..fd7a14497 --- /dev/null +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -0,0 +1,106 @@ +/* global addClass, hasClass, removeClass, onEachLazy */ + +"use strict"; + +(function() { + // Number of lines shown when code viewer is not expanded + const MAX_LINES = 10; + + // Scroll code block to the given code location + function scrollToLoc(elt, loc) { + const lines = elt.querySelector(".line-numbers"); + let scrollOffset; + + // If the block is greater than the size of the viewer, + // then scroll to the top of the block. Otherwise scroll + // to the middle of the block. + if (loc[1] - loc[0] > MAX_LINES) { + const line = Math.max(0, loc[0] - 1); + scrollOffset = lines.children[line].offsetTop; + } else { + const wrapper = elt.querySelector(".code-wrapper"); + const halfHeight = wrapper.offsetHeight / 2; + const offsetMid = (lines.children[loc[0]].offsetTop + + lines.children[loc[1]].offsetTop) / 2; + scrollOffset = offsetMid - halfHeight; + } + + lines.scrollTo(0, scrollOffset); + elt.querySelector(".rust").scrollTo(0, scrollOffset); + } + + function updateScrapedExample(example) { + const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + let locIndex = 0; + const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); + const link = example.querySelector(".scraped-example-title a"); + + if (locs.length > 1) { + // Toggle through list of examples in a given file + const onChangeLoc = changeIndex => { + removeClass(highlights[locIndex], "focus"); + changeIndex(); + scrollToLoc(example, locs[locIndex][0]); + addClass(highlights[locIndex], "focus"); + + const url = locs[locIndex][1]; + const title = locs[locIndex][2]; + + link.href = url; + link.innerHTML = title; + }; + + example.querySelector(".prev") + .addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex - 1 + locs.length) % locs.length; + }); + }); + + example.querySelector("next") + .addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex + 1) % locs.length; + }); + }); + } + + const expandButton = example.querySelector(".expand"); + if (expandButton) { + expandButton.addEventListener("click", () => { + if (hasClass(example, "expanded")) { + removeClass(example, "expanded"); + scrollToLoc(example, locs[0][0]); + } else { + addClass(example, "expanded"); + } + }); + } + + // Start with the first example in view + scrollToLoc(example, locs[0][0]); + } + + const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); + onEachLazy(firstExamples, updateScrapedExample); + onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => { + // Allow users to click the left border of the <details> section to close it, + // since the section can be large and finding the [+] button is annoying. + onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"), button => { + button.addEventListener("click", () => { + toggle.open = false; + }); + }); + + const moreExamples = toggle.querySelectorAll(".scraped-example"); + toggle.querySelector("summary").addEventListener("click", () => { + // Wrapping in setTimeout ensures the update happens after the elements are actually + // visible. This is necessary since updateScrapedExample calls scrollToLoc which + // depends on offsetHeight, a property that requires an element to be visible to + // compute correctly. + setTimeout(() => { + onEachLazy(moreExamples, updateScrapedExample); + }); + }, {once: true}); + }); +})(); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js new file mode 100644 index 000000000..75c7bd45a --- /dev/null +++ b/src/librustdoc/html/static/js/search.js @@ -0,0 +1,2297 @@ +/* global addClass, getNakedUrl, getSettingValue */ +/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */ + +"use strict"; + +(function() { +// This mapping table should match the discriminants of +// `rustdoc::formats::item_type::ItemType` type in Rust. +const itemTypes = [ + "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", +]; + +// used for special search precedence +const TY_PRIMITIVE = itemTypes.indexOf("primitive"); +const TY_KEYWORD = itemTypes.indexOf("keyword"); +const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; + +function hasOwnPropertyRustdoc(obj, property) { + return Object.prototype.hasOwnProperty.call(obj, property); +} + +// In the search display, allows to switch between tabs. +function printTab(nb) { + let iter = 0; + let foundCurrentTab = false; + let foundCurrentResultSet = false; + onEachLazy(document.getElementById("titles").childNodes, elem => { + if (nb === iter) { + addClass(elem, "selected"); + foundCurrentTab = true; + } else { + removeClass(elem, "selected"); + } + iter += 1; + }); + iter = 0; + onEachLazy(document.getElementById("results").childNodes, elem => { + if (nb === iter) { + addClass(elem, "active"); + foundCurrentResultSet = true; + } else { + removeClass(elem, "active"); + } + iter += 1; + }); + if (foundCurrentTab && foundCurrentResultSet) { + searchState.currentTab = nb; + } else if (nb !== 0) { + printTab(0); + } +} + +/** + * A function to compute the Levenshtein distance between two strings + * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported + * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode + * This code is an unmodified version of the code written by Marco de Wit + * and was found at https://stackoverflow.com/a/18514751/745719 + */ +const levenshtein_row2 = []; +function levenshtein(s1, s2) { + if (s1 === s2) { + return 0; + } + const s1_len = s1.length, s2_len = s2.length; + if (s1_len && s2_len) { + let i1 = 0, i2 = 0, a, b, c, c2; + const row = levenshtein_row2; + while (i1 < s1_len) { + row[i1] = ++i1; + } + while (i2 < s2_len) { + c2 = s2.charCodeAt(i2); + a = i2; + ++i2; + b = i2; + for (i1 = 0; i1 < s1_len; ++i1) { + c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); + a = row[i1]; + b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); + row[i1] = b; + } + } + return b; + } + return s1_len + s2_len; +} + +function initSearch(rawSearchIndex) { + const MAX_LEV_DISTANCE = 3; + const MAX_RESULTS = 200; + const NO_TYPE_FILTER = -1; + /** + * @type {Array<Row>} + */ + let searchIndex; + let currentResults; + const ALIASES = Object.create(null); + + function isWhitespace(c) { + return " \t\n\r".indexOf(c) !== -1; + } + + function isSpecialStartCharacter(c) { + return "<\"".indexOf(c) !== -1; + } + + function isEndCharacter(c) { + return ",>-".indexOf(c) !== -1; + } + + function isStopCharacter(c) { + return isWhitespace(c) || isEndCharacter(c); + } + + function isErrorCharacter(c) { + return "()".indexOf(c) !== -1; + } + + function itemTypeFromName(typename) { + for (let i = 0, len = itemTypes.length; i < len; ++i) { + if (itemTypes[i] === typename) { + return i; + } + } + + throw new Error("Unknown type filter `" + typename + "`"); + } + + /** + * If we encounter a `"`, then we try to extract the string from it until we find another `"`. + * + * This function will throw an error in the following cases: + * * There is already another string element. + * * We are parsing a generic argument. + * * There is more than one element. + * * There is no closing `"`. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {boolean} isInGenerics + */ + function getStringElem(query, parserState, isInGenerics) { + if (isInGenerics) { + throw new Error("`\"` cannot be used in generics"); + } else if (query.literalSearch) { + throw new Error("Cannot have more than one literal search element"); + } else if (parserState.totalElems - parserState.genericsElems > 0) { + throw new Error("Cannot use literal search when there is more than one element"); + } + parserState.pos += 1; + const start = parserState.pos; + const end = getIdentEndPosition(parserState); + if (parserState.pos >= parserState.length) { + throw new Error("Unclosed `\"`"); + } else if (parserState.userQuery[end] !== "\"") { + throw new Error(`Unexpected \`${parserState.userQuery[end]}\` in a string element`); + } else if (start === end) { + throw new Error("Cannot have empty string element"); + } + // To skip the quote at the end. + parserState.pos += 1; + query.literalSearch = true; + } + + /** + * Returns `true` if the current parser position is starting with "::". + * + * @param {ParserState} parserState + * + * @return {boolean} + */ + function isPathStart(parserState) { + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "::"; + } + + /** + * Returns `true` if the current parser position is starting with "->". + * + * @param {ParserState} parserState + * + * @return {boolean} + */ + function isReturnArrow(parserState) { + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "->"; + } + + /** + * Returns `true` if the given `c` character is valid for an ident. + * + * @param {string} c + * + * @return {boolean} + */ + function isIdentCharacter(c) { + return ( + c === "_" || + (c >= "0" && c <= "9") || + (c >= "a" && c <= "z") || + (c >= "A" && c <= "Z")); + } + + /** + * Returns `true` if the given `c` character is a separator. + * + * @param {string} c + * + * @return {boolean} + */ + function isSeparatorCharacter(c) { + return c === "," || isWhitespaceCharacter(c); + } + + /** + * Returns `true` if the given `c` character is a whitespace. + * + * @param {string} c + * + * @return {boolean} + */ + function isWhitespaceCharacter(c) { + return c === " " || c === "\t"; + } + + /** + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {string} name - Name of the query element. + * @param {Array<QueryElement>} generics - List of generics of this query element. + * + * @return {QueryElement} - The newly created `QueryElement`. + */ + function createQueryElement(query, parserState, name, generics, isInGenerics) { + if (name === "*" || (name.length === 0 && generics.length === 0)) { + return; + } + if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { + throw new Error("You cannot have more than one element if you use quotes"); + } + const pathSegments = name.split("::"); + if (pathSegments.length > 1) { + for (let i = 0, len = pathSegments.length; i < len; ++i) { + const pathSegment = pathSegments[i]; + + if (pathSegment.length === 0) { + if (i === 0) { + throw new Error("Paths cannot start with `::`"); + } else if (i + 1 === len) { + throw new Error("Paths cannot end with `::`"); + } + throw new Error("Unexpected `::::`"); + } + } + } + // In case we only have something like `<p>`, there is no name. + if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { + throw new Error("Found generics without a path"); + } + parserState.totalElems += 1; + if (isInGenerics) { + parserState.genericsElems += 1; + } + return { + name: name, + fullPath: pathSegments, + pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), + pathLast: pathSegments[pathSegments.length - 1], + generics: generics, + }; + } + + /** + * This function goes through all characters until it reaches an invalid ident character or the + * end of the query. It returns the position of the last character of the ident. + * + * @param {ParserState} parserState + * + * @return {integer} + */ + function getIdentEndPosition(parserState) { + let end = parserState.pos; + let foundExclamation = false; + while (parserState.pos < parserState.length) { + const c = parserState.userQuery[parserState.pos]; + if (!isIdentCharacter(c)) { + if (c === "!") { + if (foundExclamation) { + throw new Error("Cannot have more than one `!` in an ident"); + } else if (parserState.pos + 1 < parserState.length && + isIdentCharacter(parserState.userQuery[parserState.pos + 1]) + ) { + throw new Error("`!` can only be at the end of an ident"); + } + foundExclamation = true; + } else if (isErrorCharacter(c)) { + throw new Error(`Unexpected \`${c}\``); + } else if ( + isStopCharacter(c) || + isSpecialStartCharacter(c) || + isSeparatorCharacter(c) + ) { + break; + } else if (c === ":") { // If we allow paths ("str::string" for example). + if (!isPathStart(parserState)) { + break; + } + // Skip current ":". + parserState.pos += 1; + foundExclamation = false; + } else { + throw new Error(`Unexpected \`${c}\``); + } + } + parserState.pos += 1; + end = parserState.pos; + } + return end; + } + + /** + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added. + * @param {boolean} isInGenerics + */ + function getNextElem(query, parserState, elems, isInGenerics) { + const generics = []; + + let start = parserState.pos; + let end; + // We handle the strings on their own mostly to make code easier to follow. + if (parserState.userQuery[parserState.pos] === "\"") { + start += 1; + getStringElem(query, parserState, isInGenerics); + end = parserState.pos - 1; + } else { + end = getIdentEndPosition(parserState); + } + if (parserState.pos < parserState.length && + parserState.userQuery[parserState.pos] === "<" + ) { + if (isInGenerics) { + throw new Error("Unexpected `<` after `<`"); + } else if (start >= end) { + throw new Error("Found generics without a path"); + } + parserState.pos += 1; + getItemsBefore(query, parserState, generics, ">"); + } + if (start >= end && generics.length === 0) { + return; + } + elems.push( + createQueryElement( + query, + parserState, + parserState.userQuery.slice(start, end), + generics, + isInGenerics + ) + ); + } + + /** + * This function parses the next query element until it finds `endChar`, calling `getNextElem` + * to collect each element. + * + * If there is no `endChar`, this function will implicitly stop at the end without raising an + * error. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added. + * @param {string} endChar - This function will stop when it'll encounter this + * character. + */ + function getItemsBefore(query, parserState, elems, endChar) { + let foundStopChar = true; + + while (parserState.pos < parserState.length) { + const c = parserState.userQuery[parserState.pos]; + if (c === endChar) { + break; + } else if (isSeparatorCharacter(c)) { + parserState.pos += 1; + foundStopChar = true; + continue; + } else if (c === ":" && isPathStart(parserState)) { + throw new Error("Unexpected `::`: paths cannot start with `::`"); + } else if (c === ":" || isEndCharacter(c)) { + let extra = ""; + if (endChar === ">") { + extra = "`<`"; + } else if (endChar === "") { + extra = "`->`"; + } + throw new Error("Unexpected `" + c + "` after " + extra); + } + if (!foundStopChar) { + if (endChar !== "") { + throw new Error(`Expected \`,\`, \` \` or \`${endChar}\`, found \`${c}\``); + } + throw new Error(`Expected \`,\` or \` \`, found \`${c}\``); + } + const posBefore = parserState.pos; + getNextElem(query, parserState, elems, endChar === ">"); + // This case can be encountered if `getNextElem` encounted a "stop character" right from + // the start. For example if you have `,,` or `<>`. In this case, we simply move up the + // current position to continue the parsing. + if (posBefore === parserState.pos) { + parserState.pos += 1; + } + foundStopChar = false; + } + // We are either at the end of the string or on the `endChar`` character, let's move forward + // in any case. + parserState.pos += 1; + } + + /** + * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored + * if empty). + * + * @param {ParserState} parserState + */ + function checkExtraTypeFilterCharacters(parserState) { + const query = parserState.userQuery; + + for (let pos = 0; pos < parserState.pos; ++pos) { + if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) { + throw new Error(`Unexpected \`${query[pos]}\` in type filter`); + } + } + } + + /** + * Parses the provided `query` input to fill `parserState`. If it encounters an error while + * parsing `query`, it'll throw an error. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + */ + function parseInput(query, parserState) { + let c, before; + let foundStopChar = true; + + while (parserState.pos < parserState.length) { + c = parserState.userQuery[parserState.pos]; + if (isStopCharacter(c)) { + foundStopChar = true; + if (isSeparatorCharacter(c)) { + parserState.pos += 1; + continue; + } else if (c === "-" || c === ">") { + if (isReturnArrow(parserState)) { + break; + } + throw new Error(`Unexpected \`${c}\` (did you mean \`->\`?)`); + } + throw new Error(`Unexpected \`${c}\``); + } else if (c === ":" && !isPathStart(parserState)) { + if (parserState.typeFilter !== null) { + throw new Error("Unexpected `:`"); + } + if (query.elems.length === 0) { + throw new Error("Expected type filter before `:`"); + } else if (query.elems.length !== 1 || parserState.totalElems !== 1) { + throw new Error("Unexpected `:`"); + } else if (query.literalSearch) { + throw new Error("You cannot use quotes on type filter"); + } + checkExtraTypeFilterCharacters(parserState); + // The type filter doesn't count as an element since it's a modifier. + parserState.typeFilter = query.elems.pop().name; + parserState.pos += 1; + parserState.totalElems = 0; + query.literalSearch = false; + foundStopChar = true; + continue; + } + if (!foundStopChar) { + if (parserState.typeFilter !== null) { + throw new Error(`Expected \`,\`, \` \` or \`->\`, found \`${c}\``); + } + throw new Error(`Expected \`,\`, \` \`, \`:\` or \`->\`, found \`${c}\``); + } + before = query.elems.length; + getNextElem(query, parserState, query.elems, false); + if (query.elems.length === before) { + // Nothing was added, weird... Let's increase the position to not remain stuck. + parserState.pos += 1; + } + foundStopChar = false; + } + while (parserState.pos < parserState.length) { + c = parserState.userQuery[parserState.pos]; + if (isReturnArrow(parserState)) { + parserState.pos += 2; + // Get returned elements. + getItemsBefore(query, parserState, query.returned, ""); + // Nothing can come afterward! + if (query.returned.length === 0) { + throw new Error("Expected at least one item after `->`"); + } + break; + } else { + parserState.pos += 1; + } + } + } + + /** + * Takes the user search input and returns an empty `ParsedQuery`. + * + * @param {string} userQuery + * + * @return {ParsedQuery} + */ + function newParsedQuery(userQuery) { + return { + original: userQuery, + userQuery: userQuery.toLowerCase(), + typeFilter: NO_TYPE_FILTER, + elems: [], + returned: [], + // Total number of "top" elements (does not include generics). + foundElems: 0, + literalSearch: false, + error: null, + }; + } + + /** + * Build an URL with search parameters. + * + * @param {string} search - The current search being performed. + * @param {string|null} filterCrates - The current filtering crate (if any). + * + * @return {string} + */ + function buildUrl(search, filterCrates) { + let extra = "?search=" + encodeURIComponent(search); + + if (filterCrates !== null) { + extra += "&filter-crate=" + encodeURIComponent(filterCrates); + } + return getNakedUrl() + extra + window.location.hash; + } + + /** + * Return the filtering crate or `null` if there is none. + * + * @return {string|null} + */ + function getFilterCrates() { + const elem = document.getElementById("crate-search"); + + if (elem && + elem.value !== "All crates" && + hasOwnPropertyRustdoc(rawSearchIndex, elem.value) + ) { + return elem.value; + } + return null; + } + + /** + * Parses the query. + * + * The supported syntax by this parser is as follow: + * + * ident = *(ALPHA / DIGIT / "_") [!] + * path = ident *(DOUBLE-COLON ident) + * arg = path [generics] + * arg-without-generic = path + * type-sep = COMMA/WS *(COMMA/WS) + * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) + * nonempty-arg-list-without-generics = *(type-sep) arg-without-generic + * *(type-sep arg-without-generic) *(type-sep) + * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list-without-generics ] *(type-sep) + * CLOSE-ANGLE-BRACKET/EOF + * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list + * + * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] + * type-search = [type-filter *WS COLON] [ nonempty-arg-list ] [ return-args ] + * + * query = *WS (exact-search / type-search) *WS + * + * 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 = ">" + * COLON = ":" + * DOUBLE-COLON = "::" + * QUOTE = %x22 + * COMMA = "," + * RETURN-ARROW = "->" + * + * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z + * DIGIT = %x30-39 + * WS = %x09 / " " + * + * @param {string} val - The user query + * + * @return {ParsedQuery} - The parsed query + */ + function parseQuery(userQuery) { + userQuery = userQuery.trim(); + const parserState = { + length: userQuery.length, + pos: 0, + // Total number of elements (includes generics). + totalElems: 0, + genericsElems: 0, + typeFilter: null, + userQuery: userQuery.toLowerCase(), + }; + let query = newParsedQuery(userQuery); + + try { + parseInput(query, parserState); + if (parserState.typeFilter !== null) { + let typeFilter = parserState.typeFilter; + if (typeFilter === "const") { + typeFilter = "constant"; + } + query.typeFilter = itemTypeFromName(typeFilter); + } + } catch (err) { + query = newParsedQuery(userQuery); + query.error = err.message; + query.typeFilter = -1; + return query; + } + + if (!query.literalSearch) { + // If there is more than one element in the query, we switch to literalSearch in any + // case. + query.literalSearch = parserState.totalElems > 1; + } + query.foundElems = query.elems.length + query.returned.length; + return query; + } + + /** + * Creates the query results. + * + * @param {Array<Result>} results_in_args + * @param {Array<Result>} results_returned + * @param {Array<Result>} results_in_args + * @param {ParsedQuery} parsedQuery + * + * @return {ResultsTable} + */ + function createQueryResults(results_in_args, results_returned, results_others, parsedQuery) { + return { + "in_args": results_in_args, + "returned": results_returned, + "others": results_others, + "query": parsedQuery, + }; + } + + /** + * Executes the parsed query and builds a {ResultsTable}. + * + * @param {ParsedQuery} parsedQuery - The parsed user query + * @param {Object} searchWords - The list of search words to query against + * @param {Object} [filterCrates] - Crate to search in if defined + * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * + * @return {ResultsTable} + */ + function execQuery(parsedQuery, searchWords, filterCrates, currentCrate) { + const results_others = {}, results_in_args = {}, results_returned = {}; + + function transformResults(results) { + const duplicates = {}; + const out = []; + + for (const result of results) { + if (result.id > -1) { + const obj = searchIndex[result.id]; + obj.lev = result.lev; + const res = buildHrefAndPath(obj); + obj.displayPath = pathSplitter(res[0]); + obj.fullPath = obj.displayPath + obj.name; + // To be sure than it some items aren't considered as duplicate. + obj.fullPath += "|" + obj.ty; + + if (duplicates[obj.fullPath]) { + continue; + } + duplicates[obj.fullPath] = true; + + obj.href = res[1]; + out.push(obj); + if (out.length >= MAX_RESULTS) { + break; + } + } + } + return out; + } + + function sortResults(results, isType, preferredCrate) { + const userQuery = parsedQuery.userQuery; + const ar = []; + for (const entry in results) { + if (hasOwnPropertyRustdoc(results, entry)) { + const result = results[entry]; + result.word = searchWords[result.id]; + result.item = searchIndex[result.id] || {}; + ar.push(result); + } + } + results = ar; + // if there are no results then return to default and fail + if (results.length === 0) { + return []; + } + + results.sort((aaa, bbb) => { + let a, b; + + // sort by exact match with regard to the last word (mismatch goes later) + a = (aaa.word !== userQuery); + b = (bbb.word !== userQuery); + if (a !== b) { + return a - b; + } + + // Sort by non levenshtein results and then levenshtein results by the distance + // (less changes required to match means higher rankings) + a = (aaa.lev); + b = (bbb.lev); + if (a !== b) { + return a - b; + } + + // sort by crate (current crate comes first) + a = (aaa.item.crate !== preferredCrate); + b = (bbb.item.crate !== preferredCrate); + if (a !== b) { + return a - b; + } + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) { + return a - b; + } + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) { + return (a > b ? +1 : -1); + } + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { + return a - b; + } + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { + return a - b; + } + + // special precedence for primitive and keyword pages + if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || + (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { + return -1; + } + if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || + (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { + return 1; + } + + // sort by description (no description goes later) + a = (aaa.item.desc === ""); + b = (bbb.item.desc === ""); + if (a !== b) { + return a - b; + } + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) { + return a - b; + } + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) { + return (a > b ? +1 : -1); + } + + // que sera, sera + return 0; + }); + + let nameSplit = null; + if (parsedQuery.elems.length === 1) { + const hasPath = typeof parsedQuery.elems[0].path === "undefined"; + nameSplit = hasPath ? null : parsedQuery.elems[0].path; + } + + for (const result of results) { + // this validation does not make sense when searching by types + if (result.dontValidate) { + continue; + } + const name = result.item.name.toLowerCase(), + path = result.item.path.toLowerCase(), + parent = result.item.parent; + + if (!isType && !validateResult(name, path, nameSplit, parent)) { + result.id = -1; + } + } + return transformResults(results); + } + + /** + * This function checks if the object (`row`) generics match the given type (`elem`) + * generics. If there are no generics on `row`, `defaultLev` is returned. + * + * @param {Row} row - The object to check. + * @param {QueryElement} elem - The element from the parsed query. + * @param {integer} defaultLev - This is the value to return in case there are no generics. + * + * @return {integer} - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`. + */ + function checkGenerics(row, elem, defaultLev) { + if (row.generics.length === 0) { + return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1; + } else if (row.generics.length > 0 && row.generics[0].name === null) { + return checkGenerics(row.generics[0], elem, defaultLev); + } + // The names match, but we need to be sure that all generics kinda + // match as well. + let elem_name; + if (elem.generics.length > 0 && row.generics.length >= elem.generics.length) { + const elems = Object.create(null); + for (const entry of row.generics) { + elem_name = entry.name; + if (elem_name === "") { + // Pure generic, needs to check into it. + if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) { + return MAX_LEV_DISTANCE + 1; + } + continue; + } + if (elems[elem_name] === undefined) { + elems[elem_name] = 0; + } + elems[elem_name] += 1; + } + // We need to find the type that matches the most to remove it in order + // to move forward. + for (const generic of elem.generics) { + let match = null; + if (elems[generic.name]) { + match = generic.name; + } else { + for (elem_name in elems) { + if (!hasOwnPropertyRustdoc(elems, elem_name)) { + continue; + } + if (elem_name === generic) { + match = elem_name; + break; + } + } + } + if (match === null) { + return MAX_LEV_DISTANCE + 1; + } + elems[match] -= 1; + if (elems[match] === 0) { + delete elems[match]; + } + } + return 0; + } + return MAX_LEV_DISTANCE + 1; + } + + /** + * This function checks if the object (`row`) matches the given type (`elem`) and its + * generics (if any). + * + * @param {Row} row + * @param {QueryElement} elem - The element from the parsed query. + * + * @return {integer} - Returns a Levenshtein distance to the best match. + */ + function checkIfInGenerics(row, elem) { + let lev = MAX_LEV_DISTANCE + 1; + for (const entry of row.generics) { + lev = Math.min(checkType(entry, elem, true), lev); + if (lev === 0) { + break; + } + } + return lev; + } + + /** + * This function checks if the object (`row`) matches the given type (`elem`) and its + * generics (if any). + * + * @param {Row} row + * @param {QueryElement} elem - The element from the parsed query. + * @param {boolean} literalSearch + * + * @return {integer} - Returns a Levenshtein distance to the best match. If there is + * no match, returns `MAX_LEV_DISTANCE + 1`. + */ + function checkType(row, elem, literalSearch) { + if (row.name === null) { + // This is a pure "generic" search, no need to run other checks. + if (row.generics.length > 0) { + return checkIfInGenerics(row, elem); + } + return MAX_LEV_DISTANCE + 1; + } + + let lev = levenshtein(row.name, elem.name); + if (literalSearch) { + if (lev !== 0) { + // The name didn't match, let's try to check if the generics do. + if (elem.generics.length === 0) { + const checkGeneric = row.generics.length > 0; + if (checkGeneric && row.generics + .findIndex(tmp_elem => tmp_elem.name === elem.name) !== -1) { + return 0; + } + } + return MAX_LEV_DISTANCE + 1; + } else if (elem.generics.length > 0) { + return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1); + } + return 0; + } else if (row.generics.length > 0) { + if (elem.generics.length === 0) { + if (lev === 0) { + return 0; + } + // The name didn't match so we now check if the type we're looking for is inside + // the generics! + lev = checkIfInGenerics(row, elem); + // Now whatever happens, the returned distance is "less good" so we should mark + // it as such, and so we add 0.5 to the distance to make it "less good". + return lev + 0.5; + } else if (lev > MAX_LEV_DISTANCE) { + // So our item's name doesn't match at all and has generics. + // + // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're + // looking for "B<C>", we'll need to go down. + return checkIfInGenerics(row, elem); + } else { + // At this point, the name kinda match and we have generics to check, so + // let's go! + const tmp_lev = checkGenerics(row, elem, lev); + if (tmp_lev > MAX_LEV_DISTANCE) { + return MAX_LEV_DISTANCE + 1; + } + // We compute the median value of both checks and return it. + return (tmp_lev + lev) / 2; + } + } else if (elem.generics.length > 0) { + // In this case, we were expecting generics but there isn't so we simply reject this + // one. + return MAX_LEV_DISTANCE + 1; + } + // No generics on our query or on the target type so we can return without doing + // anything else. + return lev; + } + + /** + * This function checks if the object (`row`) has an argument with the given type (`elem`). + * + * @param {Row} row + * @param {QueryElement} elem - The element from the parsed query. + * @param {integer} typeFilter + * + * @return {integer} - Returns a Levenshtein distance to the best match. If there is no + * match, returns `MAX_LEV_DISTANCE + 1`. + */ + function findArg(row, elem, typeFilter) { + let lev = MAX_LEV_DISTANCE + 1; + + if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { + for (const input of row.type.inputs) { + if (!typePassesFilter(typeFilter, input.ty)) { + continue; + } + lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch)); + if (lev === 0) { + return 0; + } + } + } + return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev; + } + + /** + * This function checks if the object (`row`) returns the given type (`elem`). + * + * @param {Row} row + * @param {QueryElement} elem - The element from the parsed query. + * @param {integer} typeFilter + * + * @return {integer} - Returns a Levenshtein distance to the best match. If there is no + * match, returns `MAX_LEV_DISTANCE + 1`. + */ + function checkReturned(row, elem, typeFilter) { + let lev = MAX_LEV_DISTANCE + 1; + + if (row && row.type && row.type.output.length > 0) { + const ret = row.type.output; + for (const ret_ty of ret) { + if (!typePassesFilter(typeFilter, ret_ty.ty)) { + continue; + } + lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch)); + if (lev === 0) { + return 0; + } + } + } + return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev; + } + + function checkPath(contains, ty) { + if (contains.length === 0) { + return 0; + } + let ret_lev = MAX_LEV_DISTANCE + 1; + const path = ty.path.split("::"); + + if (ty.parent && ty.parent.name) { + path.push(ty.parent.name.toLowerCase()); + } + + const length = path.length; + const clength = contains.length; + if (clength > length) { + return MAX_LEV_DISTANCE + 1; + } + for (let i = 0; i < length; ++i) { + if (i + clength > length) { + break; + } + let lev_total = 0; + let aborted = false; + for (let x = 0; x < clength; ++x) { + const lev = levenshtein(path[i + x], contains[x]); + if (lev > MAX_LEV_DISTANCE) { + aborted = true; + break; + } + lev_total += lev; + } + if (!aborted) { + ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + } + } + return ret_lev; + } + + function typePassesFilter(filter, type) { + // No filter or Exact mach + if (filter <= NO_TYPE_FILTER || filter === type) return true; + + // Match related items + const name = itemTypes[type]; + switch (itemTypes[filter]) { + case "constant": + return name === "associatedconstant"; + case "fn": + return name === "method" || name === "tymethod"; + case "type": + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; + } + + // No match + return false; + } + + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + desc: item.desc, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + }; + } + + function handleAliases(ret, query, filterCrates, currentCrate) { + const lowerQuery = query.toLowerCase(); + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + const aliases = []; + const crateAliases = []; + if (filterCrates !== null) { + if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) { + const query_aliases = ALIASES[filterCrates][lowerQuery]; + for (const alias of query_aliases) { + aliases.push(createAliasFromItem(searchIndex[alias])); + } + } + } else { + Object.keys(ALIASES).forEach(crate => { + if (ALIASES[crate][lowerQuery]) { + const pushTo = crate === currentCrate ? crateAliases : aliases; + const query_aliases = ALIASES[crate][lowerQuery]; + for (const alias of query_aliases) { + pushTo.push(createAliasFromItem(searchIndex[alias])); + } + } + }); + } + + const sortFunc = (aaa, bbb) => { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); + + const pushFunc = alias => { + alias.alias = query; + const res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; + + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; + + aliases.forEach(pushFunc); + crateAliases.forEach(pushFunc); + } + + /** + * This function adds the given result into the provided `results` map if it matches the + * following condition: + * + * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0. + * * If it is not a "literal search", `lev` must be <= `MAX_LEV_DISTANCE`. + * + * The `results` map contains information which will be used to sort the search results: + * + * * `fullId` is a `string`` used as the key of the object we use for the `results` map. + * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element. + * * `index` is an `integer`` used to sort by the position of the word in the item's name. + * * `lev` is the main metric used to sort the search results. + * + * @param {Results} results + * @param {string} fullId + * @param {integer} id + * @param {integer} index + * @param {integer} lev + */ + function addIntoResults(results, fullId, id, index, lev) { + if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) { + if (results[fullId] !== undefined) { + const result = results[fullId]; + if (result.dontValidate || result.lev <= lev) { + return; + } + } + results[fullId] = { + id: id, + index: index, + dontValidate: parsedQuery.literalSearch, + lev: lev, + }; + } + } + + /** + * This function is called in case the query is only one element (with or without generics). + * This element will be compared to arguments' and returned values' items and also to items. + * + * Other important thing to note: since there is only one element, we use levenshtein + * distance for name comparisons. + * + * @param {Row} row + * @param {integer} pos - Position in the `searchIndex`. + * @param {QueryElement} elem - The element from the parsed query. + * @param {Results} results_others - Unqualified results (not in arguments nor in + * returned values). + * @param {Results} results_in_args - Matching arguments results. + * @param {Results} results_returned - Matching returned arguments results. + */ + function handleSingleArg( + row, + pos, + elem, + results_others, + results_in_args, + results_returned + ) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; + } + let lev, lev_add = 0, index = -1; + const fullId = row.id; + + const in_args = findArg(row, elem, parsedQuery.typeFilter); + const returned = checkReturned(row, elem, parsedQuery.typeFilter); + + addIntoResults(results_in_args, fullId, pos, index, in_args); + addIntoResults(results_returned, fullId, pos, index, returned); + + if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { + return; + } + const searchWord = searchWords[pos]; + + if (parsedQuery.literalSearch) { + if (searchWord === elem.name) { + addIntoResults(results_others, fullId, pos, -1, 0); + } + return; + } + + // No need to check anything else if it's a "pure" generics search. + if (elem.name.length === 0) { + if (row.type !== null) { + lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1); + addIntoResults(results_others, fullId, pos, index, lev); + } + return; + } + + if (elem.fullPath.length > 1) { + lev = checkPath(elem.pathWithoutLast, row); + if (lev > MAX_LEV_DISTANCE || (parsedQuery.literalSearch && lev !== 0)) { + return; + } else if (lev > 0) { + lev_add = lev / 10; + } + } + + if (searchWord.indexOf(elem.pathLast) > -1 || + row.normalizedName.indexOf(elem.pathLast) > -1 + ) { + index = row.normalizedName.indexOf(elem.pathLast); + } + lev = levenshtein(searchWord, elem.pathLast); + if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) { + if (elem.pathLast.length < 6) { + lev = 1; + } else { + lev = 0; + } + } + lev += lev_add; + if (lev > MAX_LEV_DISTANCE) { + return; + } else if (index !== -1 && elem.fullPath.length < 2) { + lev -= 1; + } + if (lev < 0) { + lev = 0; + } + addIntoResults(results_others, fullId, pos, index, lev); + } + + /** + * This function is called in case the query has more than one element. In this case, it'll + * try to match the items which validates all the elements. For `aa -> bb` will look for + * functions which have a parameter `aa` and has `bb` in its returned values. + * + * @param {Row} row + * @param {integer} pos - Position in the `searchIndex`. + * @param {Object} results + */ + function handleArgs(row, pos, results) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; + } + + let totalLev = 0; + let nbLev = 0; + + // If the result is too "bad", we return false and it ends this search. + function checkArgs(elems, callback) { + for (const elem of elems) { + // There is more than one parameter to the query so all checks should be "exact" + const lev = callback(row, elem, NO_TYPE_FILTER); + if (lev <= 1) { + nbLev += 1; + totalLev += lev; + } else { + return false; + } + } + return true; + } + if (!checkArgs(parsedQuery.elems, findArg)) { + return; + } + if (!checkArgs(parsedQuery.returned, checkReturned)) { + return; + } + + if (nbLev === 0) { + return; + } + const lev = Math.round(totalLev / nbLev); + addIntoResults(results, row.id, pos, 0, lev); + } + + function innerRunQuery() { + let elem, i, nSearchWords, in_returned, row; + + if (parsedQuery.foundElems === 1) { + if (parsedQuery.elems.length === 1) { + elem = parsedQuery.elems[0]; + for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { + // It means we want to check for this element everywhere (in names, args and + // returned). + handleSingleArg( + searchIndex[i], + i, + elem, + results_others, + results_in_args, + results_returned + ); + } + } else if (parsedQuery.returned.length === 1) { + // We received one returned argument to check, so looking into returned values. + elem = parsedQuery.returned[0]; + for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { + row = searchIndex[i]; + in_returned = checkReturned(row, elem, parsedQuery.typeFilter); + addIntoResults(results_others, row.id, i, -1, in_returned); + } + } + } else if (parsedQuery.foundElems > 0) { + for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { + handleArgs(searchIndex[i], i, results_others); + } + } + } + + if (parsedQuery.error === null) { + innerRunQuery(); + } + + const ret = createQueryResults( + sortResults(results_in_args, true, currentCrate), + sortResults(results_returned, true, currentCrate), + sortResults(results_others, false, currentCrate), + parsedQuery); + handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); + if (parsedQuery.error !== null && ret.others.length !== 0) { + // It means some doc aliases were found so let's "remove" the error! + ret.query.error = null; + } + return ret; + } + + /** + * Validate performs the following boolean logic. For example: + * "File::open" will give IF A PARENT EXISTS => ("file" && "open") + * exists in (name || path || parent) OR => ("file" && "open") exists in + * (name || path ) + * + * This could be written functionally, but I wanted to minimise + * functions on stack. + * + * @param {string} name - The name of the result + * @param {string} path - The path of the result + * @param {string} keys - The keys to be used (["file", "open"]) + * @param {Object} parent - The parent of the result + * + * @return {boolean} - Whether the result is valid or not + */ + function validateResult(name, path, keys, parent) { + if (!keys || !keys.length) { + return true; + } + for (const key of keys) { + // each check is for validation so we negate the conditions and invalidate + if (!( + // check for an exact name match + name.indexOf(key) > -1 || + // then an exact path match + path.indexOf(key) > -1 || + // next if there is a parent, check for exact parent match + (parent !== undefined && parent.name !== undefined && + parent.name.toLowerCase().indexOf(key) > -1) || + // lastly check to see if the name was a levenshtein match + levenshtein(name, key) <= MAX_LEV_DISTANCE)) { + return false; + } + } + return true; + } + + function nextTab(direction) { + const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + searchState.focusedByTab[searchState.currentTab] = document.activeElement; + printTab(next); + focusSearchResult(); + } + + // Focus the first search result on the active tab, or the result that + // was focused last time this tab was active. + function focusSearchResult() { + const target = searchState.focusedByTab[searchState.currentTab] || + document.querySelectorAll(".search-results.active a").item(0) || + document.querySelectorAll("#titles > button").item(searchState.currentTab); + if (target) { + target.focus(); + } + } + + function buildHrefAndPath(item) { + let displayPath; + let href; + const type = itemTypes[item.ty]; + const name = item.name; + let path = item.path; + + if (type === "mod") { + displayPath = path + "::"; + href = ROOT_PATH + path.replace(/::/g, "/") + "/" + + name + "/index.html"; + } else if (type === "import") { + displayPath = item.path + "::"; + href = ROOT_PATH + item.path.replace(/::/g, "/") + "/index.html#reexport." + name; + } else if (type === "primitive" || type === "keyword") { + displayPath = ""; + href = ROOT_PATH + path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } else if (type === "externcrate") { + displayPath = ""; + href = ROOT_PATH + name + "/index.html"; + } else if (item.parent !== undefined) { + const myparent = item.parent; + let anchor = "#" + type + "." + name; + const parentType = itemTypes[myparent.ty]; + let pageType = parentType; + let pageName = myparent.name; + + if (parentType === "primitive") { + displayPath = myparent.name + "::"; + } else if (type === "structfield" && parentType === "variant") { + // Structfields belonging to variants are special: the + // final path element is the enum name. + const enumNameIdx = item.path.lastIndexOf("::"); + const enumName = item.path.substr(enumNameIdx + 2); + path = item.path.substr(0, enumNameIdx); + displayPath = path + "::" + enumName + "::" + myparent.name + "::"; + anchor = "#variant." + myparent.name + ".field." + name; + pageType = "enum"; + pageName = enumName; + } else { + displayPath = path + "::" + myparent.name + "::"; + } + href = ROOT_PATH + path.replace(/::/g, "/") + + "/" + pageType + + "." + pageName + + ".html" + anchor; + } else { + displayPath = item.path + "::"; + href = ROOT_PATH + item.path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } + return [displayPath, href]; + } + + function escape(content) { + const h1 = document.createElement("h1"); + h1.textContent = content; + return h1.innerHTML; + } + + function pathSplitter(path) { + const tmp = "<span>" + path.replace(/::/g, "::</span><span>"); + if (tmp.endsWith("<span>")) { + return tmp.slice(0, tmp.length - 6); + } + return tmp; + } + + /** + * Render a set of search results for a single tab. + * @param {Array<?>} array - The search results for this tab + * @param {ParsedQuery} query + * @param {boolean} display - True if this is the active tab + */ + function addTab(array, query, display) { + let extraClass = ""; + if (display === true) { + extraClass = " active"; + } + + const output = document.createElement("div"); + let length = 0; + if (array.length > 0) { + output.className = "search-results " + extraClass; + + array.forEach(item => { + const name = item.name; + const type = itemTypes[item.ty]; + + length += 1; + + let extra = ""; + if (type === "primitive") { + extra = " <i>(primitive type)</i>"; + } else if (type === "keyword") { + extra = " <i>(keyword)</i>"; + } + + const link = document.createElement("a"); + link.className = "result-" + type; + link.href = item.href; + + const wrapper = document.createElement("div"); + const resultName = document.createElement("div"); + resultName.className = "result-name"; + + if (item.is_alias) { + const alias = document.createElement("span"); + alias.className = "alias"; + + const bold = document.createElement("b"); + bold.innerText = item.alias; + alias.appendChild(bold); + + alias.insertAdjacentHTML( + "beforeend", + "<span class=\"grey\"><i> - see </i></span>"); + + resultName.appendChild(alias); + } + resultName.insertAdjacentHTML( + "beforeend", + item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>"); + wrapper.appendChild(resultName); + + const description = document.createElement("div"); + description.className = "desc"; + const spanDesc = document.createElement("span"); + spanDesc.insertAdjacentHTML("beforeend", item.desc); + + description.appendChild(spanDesc); + wrapper.appendChild(description); + link.appendChild(wrapper); + output.appendChild(link); + }); + } else if (query.error === null) { + output.className = "search-failed" + extraClass; + output.innerHTML = "No results :(<br/>" + + "Try on <a href=\"https://duckduckgo.com/?q=" + + encodeURIComponent("rust " + query.userQuery) + + "\">DuckDuckGo</a>?<br/><br/>" + + "Or try looking in one of these:<ul><li>The <a " + + "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " + + " for technical details about the language.</li><li><a " + + "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " + + "Example</a> for expository code examples.</a></li><li>The <a " + + "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " + + "introductions to language features and the language itself.</li><li><a " + + "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" + + " <a href=\"https://crates.io/\">crates.io</a>.</li></ul>"; + } + return [output, length]; + } + + function makeTabHeader(tabNb, text, nbElems) { + if (searchState.currentTab === tabNb) { + return "<button class=\"selected\">" + text + + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + + /** + * @param {ResultsTable} results + * @param {boolean} go_to_first + * @param {string} filterCrates + */ + function showResults(results, go_to_first, filterCrates) { + const search = searchState.outputElement(); + if (go_to_first || (results.others.length === 1 + && getSettingValue("go-to-only-result") === "true" + // By default, the search DOM element is "empty" (meaning it has no children not + // text content). Once a search has been run, it won't be empty, even if you press + // ESC or empty the search input (which also "cancels" the search). + && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)) + ) { + const elem = document.createElement("a"); + elem.href = results.others[0].href; + removeClass(elem, "active"); + // For firefox, we need the element to be in the DOM so it can be clicked. + document.body.appendChild(elem); + elem.click(); + return; + } + if (results.query === undefined) { + results.query = parseQuery(searchState.input.value); + } + + currentResults = results.query.userQuery; + + const ret_others = addTab(results.others, results.query, true); + const ret_in_args = addTab(results.in_args, results.query, false); + const ret_returned = addTab(results.returned, results.query, false); + + // Navigate to the relevant tab if the current tab is empty, like in case users search + // for "-> String". If they had selected another tab previously, they have to click on + // it again. + let currentTab = searchState.currentTab; + if ((currentTab === 0 && ret_others[1] === 0) || + (currentTab === 1 && ret_in_args[1] === 0) || + (currentTab === 2 && ret_returned[1] === 0)) { + if (ret_others[1] !== 0) { + currentTab = 0; + } else if (ret_in_args[1] !== 0) { + currentTab = 1; + } else if (ret_returned[1] !== 0) { + currentTab = 2; + } + } + + let crates = ""; + const crates_list = Object.keys(rawSearchIndex); + if (crates_list.length > 1) { + crates = " in <select id=\"crate-search\"><option value=\"All crates\">" + + "All crates</option>"; + for (const c of crates_list) { + crates += `<option value="${c}" ${c === filterCrates && "selected"}>${c}</option>`; + } + crates += "</select>"; + } + + let typeFilter = ""; + if (results.query.typeFilter !== NO_TYPE_FILTER) { + typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")"; + } + + let output = "<div id=\"search-settings\">" + + `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` + + `${typeFilter}</h1>${crates}</div>`; + if (results.query.error !== null) { + output += `<h3>Query parser error: "${results.query.error}".</h3>`; + output += "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + "</div>"; + currentTab = 0; + } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { + output += "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(1, "In Parameters", ret_in_args[1]) + + makeTabHeader(2, "In Return Types", ret_returned[1]) + + "</div>"; + } else { + const signatureTabTitle = + results.query.elems.length === 0 ? "In Function Return Types" : + results.query.returned.length === 0 ? "In Function Parameters" : + "In Function Signatures"; + output += "<div id=\"titles\">" + + makeTabHeader(0, signatureTabTitle, ret_others[1]) + + "</div>"; + currentTab = 0; + } + + const resultsElem = document.createElement("div"); + resultsElem.id = "results"; + resultsElem.appendChild(ret_others[0]); + resultsElem.appendChild(ret_in_args[0]); + resultsElem.appendChild(ret_returned[0]); + + search.innerHTML = output; + const crateSearch = document.getElementById("crate-search"); + if (crateSearch) { + crateSearch.addEventListener("input", updateCrate); + } + search.appendChild(resultsElem); + // Reset focused elements. + searchState.showResults(search); + const elems = document.getElementById("titles").childNodes; + searchState.focusedByTab = []; + let i = 0; + for (const elem of elems) { + const j = i; + elem.onclick = () => printTab(j); + searchState.focusedByTab.push(null); + i += 1; + } + printTab(currentTab); + } + + /** + * Perform a search based on the current state of the search input element + * and display the results. + * @param {Event} [e] - The event that triggered this search, if any + * @param {boolean} [forced] + */ + function search(e, forced) { + const params = searchState.getQueryStringParams(); + const query = parseQuery(searchState.input.value.trim()); + + if (e) { + e.preventDefault(); + } + + if (!forced && query.userQuery === currentResults) { + if (query.userQuery.length > 0) { + putBackSearch(); + } + return; + } + + let filterCrates = getFilterCrates(); + + // In case we have no information about the saved crate and there is a URL query parameter, + // we override it with the URL query parameter. + if (filterCrates === null && params["filter-crate"] !== undefined) { + filterCrates = params["filter-crate"]; + } + + // Update document title to maintain a meaningful browser history + searchState.title = "Results for " + query.original + " - Rust"; + + // Because searching is incremental by character, only the most + // recent search query is added to the browser history. + if (browserSupportsHistoryApi()) { + const newURL = buildUrl(query.original, filterCrates); + + if (!history.state && !params.search) { + history.pushState(null, "", newURL); + } else { + history.replaceState(null, "", newURL); + } + } + + showResults( + execQuery(query, searchWords, filterCrates, window.currentCrate), + params.go_to_first, + filterCrates); + } + + /** + * Convert a list of RawFunctionType / ID to object-based FunctionType. + * + * Crates often have lots of functions in them, and it's common to have a large number of + * functions that operate on a small set of data types, so the search index compresses them + * by encoding function parameter and return types as indexes into an array of names. + * + * Even when a general-purpose compression algorithm is used, this is still a win. I checked. + * https://github.com/rust-lang/rust/pull/98475#issue-1284395985 + * + * The format for individual function types is encoded in + * librustdoc/html/render/mod.rs: impl Serialize for RenderType + * + * @param {null|Array<RawFunctionType>} types + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {Array<FunctionSearchType>} + */ + function buildItemSearchTypeAll(types, lowercasePaths) { + const PATH_INDEX_DATA = 0; + const GENERICS_DATA = 1; + return types.map(type => { + let pathIndex, generics; + if (typeof type === "number") { + pathIndex = type; + generics = []; + } else { + pathIndex = type[PATH_INDEX_DATA]; + generics = buildItemSearchTypeAll(type[GENERICS_DATA], lowercasePaths); + } + return { + // `0` is used as a sentinel because it's fewer bytes than `null` + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: generics, + }; + }); + } + + /** + * Convert from RawFunctionSearchType to FunctionSearchType. + * + * Crates often have lots of functions in them, and function signatures are sometimes complex, + * so rustdoc uses a pretty tight encoding for them. This function converts it to a simpler, + * object-based encoding so that the actual search code is more readable and easier to debug. + * + * The raw function search type format is generated using serde in + * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType + * + * @param {RawFunctionSearchType} functionSearchType + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {null|FunctionSearchType} + */ + function buildFunctionSearchType(functionSearchType, lowercasePaths) { + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + // `0` is used as a sentinel because it's fewer bytes than `null` + if (functionSearchType === 0) { + return null; + } + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + const pathIndex = functionSearchType[INPUTS_DATA]; + inputs = [{ + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: [], + }]; + } else { + inputs = buildItemSearchTypeAll(functionSearchType[INPUTS_DATA], lowercasePaths); + } + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + const pathIndex = functionSearchType[OUTPUT_DATA]; + output = [{ + name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name, + ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty, + generics: [], + }]; + } else { + output = buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA], lowercasePaths); + } + } else { + output = []; + } + return { + inputs, output, + }; + } + + function buildIndex(rawSearchIndex) { + searchIndex = []; + /** + * @type {Array<string>} + */ + const searchWords = []; + let i, word; + let currentIndex = 0; + let id = 0; + + for (const crate in rawSearchIndex) { + if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) { + continue; + } + + 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 small integer that represents an + * offset in `itemTypes`). d[i] contains the description of that item. + * + * q[i] contains the full path of the item, or an empty string indicating + * "same as q[i-1]". + * + * i[i] contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * f[i] contains function signatures, or `0` if the item isn't a function. + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a list of path/type pairs. It is used for parents and function parameters. + * + * @type {{ + * doc: string, + * a: Object, + * n: Array<string>, + * t: Array<Number>, + * d: Array<string>, + * q: Array<string>, + * i: Array<Number>, + * f: Array<RawFunctionSearchType>, + * p: Array<Object>, + * }} + */ + const crateCorpus = rawSearchIndex[crate]; + + searchWords.push(crate); + // This object should have exactly the same set of fields as the "row" + // object defined below. Your JavaScript runtime will thank you. + // https://mathiasbynens.be/notes/shapes-ics + const crateRow = { + crate: crate, + ty: 1, // == ExternCrate + name: crate, + path: "", + desc: crateCorpus.doc, + parent: undefined, + type: null, + id: id, + normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), + }; + id += 1; + searchIndex.push(crateRow); + currentIndex += 1; + + // an array of (Number) item types + const itemTypes = crateCorpus.t; + // an array of (String) item names + const itemNames = crateCorpus.n; + // an array of (String) full paths (or empty string for previous path) + const itemPaths = crateCorpus.q; + // an array of (String) descriptions + const itemDescs = crateCorpus.d; + // an array of (Number) the parent path index + 1 to `paths`, or 0 if none + const itemParentIdxs = crateCorpus.i; + // an array of (Object | null) the type of the function, if any + const itemFunctionSearchTypes = crateCorpus.f; + // an array of [(Number) item type, + // (String) name] + const paths = crateCorpus.p; + // an array of [(String) alias name + // [Number] index to items] + const aliases = crateCorpus.a; + + // an array of [{name: String, ty: Number}] + const lowercasePaths = []; + + // convert `rawPaths` entries into object form + // generate normalizedPaths for function search mode + let len = paths.length; + for (i = 0; i < len; ++i) { + lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()}); + paths[i] = {ty: paths[i][0], name: paths[i][1]}; + } + + // convert `item*` into an object form, and construct word indices. + // + // before any analysis is performed lets gather the search terms to + // search against apart from the rest of the data. This is a quick + // 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 + len = itemTypes.length; + let lastPath = ""; + for (i = 0; i < len; ++i) { + // This object should have exactly the same set of fields as the "crateRow" + // object defined above. + if (typeof itemNames[i] === "string") { + word = itemNames[i].toLowerCase(); + searchWords.push(word); + } else { + word = ""; + searchWords.push(""); + } + const row = { + crate: crate, + ty: itemTypes[i], + name: itemNames[i], + path: itemPaths[i] ? itemPaths[i] : lastPath, + desc: itemDescs[i], + parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, + type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths), + id: id, + normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), + }; + id += 1; + searchIndex.push(row); + lastPath = row.path; + crateSize += 1; + } + + if (aliases) { + ALIASES[crate] = Object.create(null); + for (const alias_name in aliases) { + if (!hasOwnPropertyRustdoc(aliases, alias_name)) { + continue; + } + + if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) { + ALIASES[crate][alias_name] = []; + } + for (const local_alias of aliases[alias_name]) { + ALIASES[crate][alias_name].push(local_alias + currentIndex); + } + } + } + currentIndex += crateSize; + } + return searchWords; + } + + /** + * Callback for when the search form is submitted. + * @param {Event} [e] - The event that triggered this call, if any + */ + function onSearchSubmit(e) { + e.preventDefault(); + searchState.clearInputTimeout(); + search(); + } + + function putBackSearch() { + const search_input = searchState.input; + if (!searchState.input) { + return; + } + if (search_input.value !== "" && !searchState.isDisplayed()) { + searchState.showResults(); + if (browserSupportsHistoryApi()) { + history.replaceState(null, "", + buildUrl(search_input.value, getFilterCrates())); + } + document.title = searchState.title; + } + } + + function registerSearchEvents() { + const params = searchState.getQueryStringParams(); + + // Populate search bar with query string search term when provided, + // but only if the input bar is empty. This avoid the obnoxious issue + // where you start trying to do a search, and the index loads, and + // suddenly your search is gone! + if (searchState.input.value === "") { + searchState.input.value = params.search || ""; + } + + const searchAfter500ms = () => { + searchState.clearInputTimeout(); + if (searchState.input.value.length === 0) { + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + searchState.hideResults(); + } else { + searchState.timeout = setTimeout(search, 500); + } + }; + searchState.input.onkeyup = searchAfter500ms; + searchState.input.oninput = searchAfter500ms; + document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; + searchState.input.onchange = e => { + if (e.target !== document.activeElement) { + // To prevent doing anything when it's from a blur event. + return; + } + // Do NOT e.preventDefault() here. It will prevent pasting. + searchState.clearInputTimeout(); + // zero-timeout necessary here because at the time of event handler execution the + // pasted content is not in the input field yet. Shouldn’t make any difference for + // change, though. + setTimeout(search, 0); + }; + searchState.input.onpaste = searchState.input.onchange; + + searchState.outputElement().addEventListener("keydown", e => { + // We only handle unmodified keystrokes here. We don't want to interfere with, + // for instance, alt-left and alt-right for history navigation. + if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { + return; + } + // up and down arrow select next/previous search result, or the + // search box if we're already at the top. + if (e.which === 38) { // up + const previous = document.activeElement.previousElementSibling; + if (previous) { + previous.focus(); + } else { + searchState.focus(); + } + e.preventDefault(); + } else if (e.which === 40) { // down + const next = document.activeElement.nextElementSibling; + if (next) { + next.focus(); + } + const rect = document.activeElement.getBoundingClientRect(); + if (window.innerHeight - rect.bottom < rect.height) { + window.scrollBy(0, rect.height); + } + e.preventDefault(); + } else if (e.which === 37) { // left + nextTab(-1); + e.preventDefault(); + } else if (e.which === 39) { // right + nextTab(1); + e.preventDefault(); + } + }); + + searchState.input.addEventListener("keydown", e => { + if (e.which === 40) { // down + focusSearchResult(); + e.preventDefault(); + } + }); + + searchState.input.addEventListener("focus", () => { + putBackSearch(); + }); + + searchState.input.addEventListener("blur", () => { + searchState.input.placeholder = searchState.input.origPlaceholder; + }); + + // Push and pop states are used to add search results to the browser + // history. + if (browserSupportsHistoryApi()) { + // Store the previous <title> so we can revert back to it later. + const previousTitle = document.title; + + window.addEventListener("popstate", e => { + const params = searchState.getQueryStringParams(); + // Revert to the previous title manually since the History + // API ignores the title parameter. + document.title = previousTitle; + // When browsing forward to search results the previous + // search will be repeated, so the currentResults are + // cleared to ensure the search is successful. + currentResults = null; + // Synchronize search bar with query string state and + // perform the search. This will empty the bar if there's + // nothing there, which lets you really go back to a + // previous state with nothing in the bar. + if (params.search && params.search.length > 0) { + searchState.input.value = params.search; + // Some browsers fire "onpopstate" for every page load + // (Chrome), while others fire the event only when actually + // popping a state (Firefox), which is why search() is + // called both here and at the end of the startSearch() + // function. + search(e); + } else { + searchState.input.value = ""; + // When browsing back from search results the main page + // visibility must be reset. + searchState.hideResults(); + } + }); + } + + // This is required in firefox to avoid this problem: Navigating to a search result + // with the keyboard, hitting enter, and then hitting back would take you back to + // the doc page, rather than the search that should overlay it. + // This was an interaction between the back-forward cache and our handlers + // that try to sync state between the URL and the search input. To work around it, + // do a small amount of re-init on page show. + window.onpageshow = () => { + const qSearch = searchState.getQueryStringParams().search; + if (searchState.input.value === "" && qSearch) { + searchState.input.value = qSearch; + } + search(); + }; + } + + function updateCrate(ev) { + if (ev.target.value === "All crates") { + // If we don't remove it from the URL, it'll be picked up again by the search. + const params = searchState.getQueryStringParams(); + const query = searchState.input.value.trim(); + if (!history.state && !params.search) { + history.pushState(null, "", buildUrl(query, null)); + } else { + history.replaceState(null, "", buildUrl(query, null)); + } + } + // In case you "cut" the entry from the search input, then change the crate filter + // before paste back the previous search, you get the old search results without + // the filter. To prevent this, we need to remove the previous results. + currentResults = null; + search(undefined, true); + } + + /** + * @type {Array<string>} + */ + const searchWords = buildIndex(rawSearchIndex); + if (typeof window !== "undefined") { + registerSearchEvents(); + // If there's a search term in the URL, execute the search now. + if (window.searchState.getQueryStringParams().search) { + search(); + } + } + + if (typeof exports !== "undefined") { + exports.initSearch = initSearch; + exports.execQuery = execQuery; + exports.parseQuery = parseQuery; + } + return searchWords; +} + +if (typeof window !== "undefined") { + window.initSearch = initSearch; + if (window.searchIndex !== undefined) { + initSearch(window.searchIndex); + } +} else { + // Running in Node, not a browser. Run initSearch just to produce the + // exports. + initSearch({}); +} + + +})(); diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js new file mode 100644 index 000000000..797b931af --- /dev/null +++ b/src/librustdoc/html/static/js/settings.js @@ -0,0 +1,272 @@ +// Local js definitions: +/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */ +/* global addClass, removeClass, onEach, onEachLazy, blurHandler, elemIsInParent */ +/* global MAIN_ID, getVar, getSettingsButton */ + +"use strict"; + +(function() { + const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + + function changeSetting(settingName, value) { + updateLocalStorage(settingName, value); + + switch (settingName) { + case "theme": + case "preferred-dark-theme": + case "preferred-light-theme": + case "use-system-theme": + updateSystemTheme(); + updateLightAndDark(); + break; + } + } + + function handleKey(ev) { + // Don't interfere with browser shortcuts + if (ev.ctrlKey || ev.altKey || ev.metaKey) { + return; + } + switch (getVirtualKey(ev)) { + case "Enter": + case "Return": + case "Space": + ev.target.checked = !ev.target.checked; + ev.preventDefault(); + break; + } + } + + function showLightAndDark() { + addClass(document.getElementById("theme").parentElement, "hidden"); + removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); + removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + } + + function hideLightAndDark() { + addClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); + addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + removeClass(document.getElementById("theme").parentElement, "hidden"); + } + + function updateLightAndDark() { + if (getSettingValue("use-system-theme") !== "false") { + showLightAndDark(); + } else { + hideLightAndDark(); + } + } + + function setEvents(settingsElement) { + updateLightAndDark(); + onEachLazy(settingsElement.getElementsByClassName("slider"), elem => { + const toggle = elem.previousElementSibling; + const settingId = toggle.id; + const settingValue = getSettingValue(settingId); + if (settingValue !== null) { + toggle.checked = settingValue === "true"; + } + toggle.onchange = function() { + changeSetting(this.id, this.checked); + }; + toggle.onkeyup = handleKey; + toggle.onkeyrelease = handleKey; + }); + onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => { + const select = elem.getElementsByTagName("select")[0]; + const settingId = select.id; + const settingValue = getSettingValue(settingId); + if (settingValue !== null) { + select.value = settingValue; + } + select.onchange = function() { + changeSetting(this.id, this.value); + }; + }); + onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { + const settingId = elem.name; + const settingValue = getSettingValue(settingId); + if (settingValue !== null && settingValue !== "null") { + elem.checked = settingValue === elem.value; + } + elem.addEventListener("change", ev => { + changeSetting(ev.target.name, ev.target.value); + }); + }); + } + + /** + * This function builds the sections inside the "settings page". It takes a `settings` list + * as argument which describes each setting and how to render it. It returns a string + * representing the raw HTML. + * + * @param {Array<Object>} settings + * + * @return {string} + */ + function buildSettingsPageSections(settings) { + let output = ""; + + for (const setting of settings) { + output += "<div class=\"setting-line\">"; + const js_data_name = setting["js_name"]; + const setting_name = setting["name"]; + + if (setting["options"] !== undefined) { + // This is a select setting. + output += `<div class="radio-line" id="${js_data_name}">\ + <span class="setting-name">${setting_name}</span>\ + <div class="choices">`; + onEach(setting["options"], option => { + const checked = option === setting["default"] ? " checked" : ""; + + output += `<label for="${js_data_name}-${option}" class="choice">\ + <input type="radio" name="${js_data_name}" \ + id="${js_data_name}-${option}" value="${option}"${checked}>\ + <span>${option}</span>\ + </label>`; + }); + output += "</div></div>"; + } else { + // This is a toggle. + const checked = setting["default"] === true ? " checked" : ""; + output += `<label class="toggle">\ + <input type="checkbox" id="${js_data_name}"${checked}>\ + <span class="slider"></span>\ + <span class="label">${setting_name}</span>\ + </label>`; + } + output += "</div>"; + } + return output; + } + + /** + * This function builds the "settings page" and returns the generated HTML element. + * + * @return {HTMLElement} + */ + function buildSettingsPage() { + const themes = getVar("themes").split(","); + const settings = [ + { + "name": "Use system theme", + "js_name": "use-system-theme", + "default": true, + }, + { + "name": "Theme", + "js_name": "theme", + "default": "light", + "options": themes, + }, + { + "name": "Preferred light theme", + "js_name": "preferred-light-theme", + "default": "light", + "options": themes, + }, + { + "name": "Preferred dark theme", + "js_name": "preferred-dark-theme", + "default": "dark", + "options": themes, + }, + { + "name": "Auto-hide item contents for large items", + "js_name": "auto-hide-large-items", + "default": true, + }, + { + "name": "Auto-hide item methods' documentation", + "js_name": "auto-hide-method-docs", + "default": false, + }, + { + "name": "Auto-hide trait implementation documentation", + "js_name": "auto-hide-trait-implementations", + "default": false, + }, + { + "name": "Directly go to item in search if there is only one result", + "js_name": "go-to-only-result", + "default": false, + }, + { + "name": "Show line numbers on code examples", + "js_name": "line-numbers", + "default": false, + }, + { + "name": "Disable keyboard shortcuts", + "js_name": "disable-shortcuts", + "default": false, + }, + ]; + + // Then we build the DOM. + const elementKind = isSettingsPage ? "section" : "div"; + const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`; + const el = document.createElement(elementKind); + el.id = "settings"; + el.className = "popover"; + el.innerHTML = innerHTML; + + if (isSettingsPage) { + document.getElementById(MAIN_ID).appendChild(el); + } else { + el.setAttribute("tabindex", "-1"); + getSettingsButton().appendChild(el); + } + return el; + } + + const settingsMenu = buildSettingsPage(); + + function displaySettings() { + settingsMenu.style.display = ""; + } + + function settingsBlurHandler(event) { + blurHandler(event, getSettingsButton(), window.hidePopoverMenus); + } + + if (isSettingsPage) { + // We replace the existing "onclick" callback to do nothing if clicked. + getSettingsButton().onclick = function(event) { + event.preventDefault(); + }; + } else { + // We replace the existing "onclick" callback. + const settingsButton = getSettingsButton(); + const settingsMenu = document.getElementById("settings"); + settingsButton.onclick = function(event) { + if (elemIsInParent(event.target, settingsMenu)) { + return; + } + event.preventDefault(); + const shouldDisplaySettings = settingsMenu.style.display === "none"; + + window.hidePopoverMenus(); + if (shouldDisplaySettings) { + displaySettings(); + } + }; + settingsButton.onblur = settingsBlurHandler; + settingsButton.querySelector("a").onblur = settingsBlurHandler; + onEachLazy(settingsMenu.querySelectorAll("input"), el => { + el.onblur = settingsBlurHandler; + }); + settingsMenu.onblur = settingsBlurHandler; + } + + // We now wait a bit for the web browser to end re-computing the DOM... + setTimeout(() => { + setEvents(settingsMenu); + // The setting menu is already displayed if we're on the settings page. + if (!isSettingsPage) { + displaySettings(); + } + removeClass(getSettingsButton(), "rotate"); + }, 0); +})(); diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js new file mode 100644 index 000000000..c45d61429 --- /dev/null +++ b/src/librustdoc/html/static/js/source-script.js @@ -0,0 +1,241 @@ +// From rust: +/* global sourcesIndex */ + +// Local js definitions: +/* global addClass, getCurrentValue, onEachLazy, removeClass, browserSupportsHistoryApi */ +/* global updateLocalStorage */ + +"use strict"; + +(function() { + +const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value; +let oldScrollPosition = 0; + +const NAME_OFFSET = 0; +const DIRS_OFFSET = 1; +const FILES_OFFSET = 2; + +function closeSidebarIfMobile() { + if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) { + updateLocalStorage("source-sidebar-show", "false"); + } +} + +function createDirEntry(elem, parent, fullPath, hasFoundFile) { + const dirEntry = document.createElement("details"); + const summary = document.createElement("summary"); + + dirEntry.className = "dir-entry"; + + fullPath += elem[NAME_OFFSET] + "/"; + + summary.innerText = elem[NAME_OFFSET]; + dirEntry.appendChild(summary); + + const folders = document.createElement("div"); + folders.className = "folders"; + if (elem[DIRS_OFFSET]) { + for (const dir of elem[DIRS_OFFSET]) { + if (createDirEntry(dir, folders, fullPath, false)) { + dirEntry.open = true; + hasFoundFile = true; + } + } + } + dirEntry.appendChild(folders); + + const files = document.createElement("div"); + files.className = "files"; + if (elem[FILES_OFFSET]) { + for (const file_text of elem[FILES_OFFSET]) { + const file = document.createElement("a"); + file.innerText = file_text; + file.href = rootPath + "src/" + fullPath + file_text + ".html"; + file.addEventListener("click", closeSidebarIfMobile); + const w = window.location.href.split("#")[0]; + if (!hasFoundFile && w === file.href) { + file.className = "selected"; + dirEntry.open = true; + hasFoundFile = true; + } + files.appendChild(file); + } + } + dirEntry.appendChild(files); + parent.appendChild(dirEntry); + return hasFoundFile; +} + +function toggleSidebar() { + const child = this.parentNode.children[0]; + if (child.innerText === ">") { + if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) { + // This is to keep the scroll position on mobile. + oldScrollPosition = window.scrollY; + document.body.style.position = "fixed"; + document.body.style.top = `-${oldScrollPosition}px`; + } + addClass(document.documentElement, "source-sidebar-expanded"); + child.innerText = "<"; + updateLocalStorage("source-sidebar-show", "true"); + } else { + if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) { + // This is to keep the scroll position on mobile. + document.body.style.position = ""; + document.body.style.top = ""; + // The scroll position is lost when resetting the style, hence why we store it in + // `oldScroll`. + window.scrollTo(0, oldScrollPosition); + } + removeClass(document.documentElement, "source-sidebar-expanded"); + child.innerText = ">"; + updateLocalStorage("source-sidebar-show", "false"); + } +} + +function createSidebarToggle() { + const sidebarToggle = document.createElement("div"); + sidebarToggle.id = "sidebar-toggle"; + + const inner = document.createElement("button"); + + if (getCurrentValue("source-sidebar-show") === "true") { + inner.innerText = "<"; + } else { + inner.innerText = ">"; + } + inner.onclick = toggleSidebar; + + sidebarToggle.appendChild(inner); + return sidebarToggle; +} + +// This function is called from "source-files.js", generated in `html/render/mod.rs`. +// eslint-disable-next-line no-unused-vars +function createSourceSidebar() { + const container = document.querySelector("nav.sidebar"); + + const sidebarToggle = createSidebarToggle(); + container.insertBefore(sidebarToggle, container.firstChild); + + const sidebar = document.createElement("div"); + sidebar.id = "source-sidebar"; + + let hasFoundFile = false; + + const title = document.createElement("div"); + title.className = "title"; + title.innerText = "Files"; + sidebar.appendChild(title); + Object.keys(sourcesIndex).forEach(key => { + sourcesIndex[key][NAME_OFFSET] = key; + hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", + hasFoundFile); + }); + + container.appendChild(sidebar); + // Focus on the current file in the source files sidebar. + const selected_elem = sidebar.getElementsByClassName("selected")[0]; + if (typeof selected_elem !== "undefined") { + selected_elem.focus(); + } +} + +const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/; + +function highlightSourceLines(match) { + if (typeof match === "undefined") { + match = window.location.hash.match(lineNumbersRegex); + } + if (!match) { + return; + } + let from = parseInt(match[1], 10); + let to = from; + if (typeof match[2] !== "undefined") { + to = parseInt(match[2], 10); + } + if (to < from) { + const tmp = to; + to = from; + from = tmp; + } + let elem = document.getElementById(from); + if (!elem) { + return; + } + const x = document.getElementById(from); + if (x) { + x.scrollIntoView(); + } + onEachLazy(document.getElementsByClassName("line-numbers"), e => { + onEachLazy(e.getElementsByTagName("span"), i_e => { + removeClass(i_e, "line-highlighted"); + }); + }); + for (let i = from; i <= to; ++i) { + elem = document.getElementById(i); + if (!elem) { + break; + } + addClass(elem, "line-highlighted"); + } +} + +const handleSourceHighlight = (function() { + let prev_line_id = 0; + + const set_fragment = name => { + const x = window.scrollX, + y = window.scrollY; + if (browserSupportsHistoryApi()) { + history.replaceState(null, null, "#" + name); + highlightSourceLines(); + } else { + location.replace("#" + name); + } + // Prevent jumps when selecting one or many lines + window.scrollTo(x, y); + }; + + return ev => { + let cur_line_id = parseInt(ev.target.id, 10); + // It can happen when clicking not on a line number span. + if (isNaN(cur_line_id)) { + return; + } + ev.preventDefault(); + + if (ev.shiftKey && prev_line_id) { + // Swap selection if needed + if (prev_line_id > cur_line_id) { + const tmp = prev_line_id; + prev_line_id = cur_line_id; + cur_line_id = tmp; + } + + set_fragment(prev_line_id + "-" + cur_line_id); + } else { + prev_line_id = cur_line_id; + + set_fragment(cur_line_id); + } + }; +}()); + +window.addEventListener("hashchange", () => { + const match = window.location.hash.match(lineNumbersRegex); + if (match) { + return highlightSourceLines(match); + } +}); + +onEachLazy(document.getElementsByClassName("line-numbers"), el => { + el.addEventListener("click", handleSourceHighlight); +}); + +highlightSourceLines(); + +window.createSourceSidebar = createSourceSidebar; +})(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js new file mode 100644 index 000000000..0c5389d45 --- /dev/null +++ b/src/librustdoc/html/static/js/storage.js @@ -0,0 +1,268 @@ +// storage.js is loaded in the `<head>` of all rustdoc pages and doesn't +// use `async` or `defer`. That means it blocks further parsing and rendering +// of the page: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script. +// This makes it the correct place to act on settings that affect the display of +// the page, so we don't see major layout changes during the load of the page. +"use strict"; + +const darkThemes = ["dark", "ayu"]; +window.currentTheme = document.getElementById("themeStyle"); +window.mainTheme = document.getElementById("mainThemeStyle"); + +// WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY +// If you update this line, then you also need to update the two media queries with the same +// warning in rustdoc.css +window.RUSTDOC_MOBILE_BREAKPOINT = 701; + +const settingsDataset = (function() { + const settingsElement = document.getElementById("default-settings"); + if (settingsElement === null) { + return null; + } + const dataset = settingsElement.dataset; + if (dataset === undefined) { + return null; + } + return dataset; +})(); + +function getSettingValue(settingName) { + const current = getCurrentValue(settingName); + if (current !== null) { + return current; + } + if (settingsDataset !== null) { + // See the comment for `default_settings.into_iter()` etc. in + // `Options::from_matches` in `librustdoc/config.rs`. + const def = settingsDataset[settingName.replace(/-/g,"_")]; + if (def !== undefined) { + return def; + } + } + return null; +} + +const localStoredTheme = getSettingValue("theme"); + +const savedHref = []; + +// eslint-disable-next-line no-unused-vars +function hasClass(elem, className) { + return elem && elem.classList && elem.classList.contains(className); +} + +// eslint-disable-next-line no-unused-vars +function addClass(elem, className) { + if (!elem || !elem.classList) { + return; + } + elem.classList.add(className); +} + +// eslint-disable-next-line no-unused-vars +function removeClass(elem, className) { + if (!elem || !elem.classList) { + return; + } + elem.classList.remove(className); +} + +/** + * Run a callback for every element of an Array. + * @param {Array<?>} arr - The array to iterate over + * @param {function(?)} func - The callback + * @param {boolean} [reversed] - Whether to iterate in reverse + */ +function onEach(arr, func, reversed) { + if (arr && arr.length > 0 && func) { + if (reversed) { + const length = arr.length; + for (let i = length - 1; i >= 0; --i) { + if (func(arr[i])) { + return true; + } + } + } else { + for (const elem of arr) { + if (func(elem)) { + return true; + } + } + } + } + return false; +} + +/** + * Turn an HTMLCollection or a NodeList into an Array, then run a callback + * for every element. This is useful because iterating over an HTMLCollection + * or a "live" NodeList while modifying it can be very slow. + * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection + * https://developer.mozilla.org/en-US/docs/Web/API/NodeList + * @param {NodeList<?>|HTMLCollection<?>} lazyArray - An array to iterate over + * @param {function(?)} func - The callback + * @param {boolean} [reversed] - Whether to iterate in reverse + */ +function onEachLazy(lazyArray, func, reversed) { + return onEach( + Array.prototype.slice.call(lazyArray), + func, + reversed); +} + +function updateLocalStorage(name, value) { + try { + window.localStorage.setItem("rustdoc-" + name, value); + } catch (e) { + // localStorage is not accessible, do nothing + } +} + +function getCurrentValue(name) { + try { + return window.localStorage.getItem("rustdoc-" + name); + } catch (e) { + return null; + } +} + +function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { + const newHref = mainStyleElem.href.replace( + /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css"); + + // If this new value comes from a system setting or from the previously + // saved theme, no need to save it. + if (saveTheme) { + updateLocalStorage("theme", newTheme); + } + + if (styleElem.href === newHref) { + return; + } + + let found = false; + if (savedHref.length === 0) { + onEachLazy(document.getElementsByTagName("link"), el => { + savedHref.push(el.href); + }); + } + onEach(savedHref, el => { + if (el === newHref) { + found = true; + return true; + } + }); + if (found) { + styleElem.href = newHref; + } +} + +// This function is called from "main.js". +// eslint-disable-next-line no-unused-vars +function useSystemTheme(value) { + if (value === undefined) { + value = true; + } + + updateLocalStorage("use-system-theme", value); + + // update the toggle if we're on the settings page + const toggle = document.getElementById("use-system-theme"); + if (toggle && toggle instanceof HTMLInputElement) { + toggle.checked = value; + } +} + +const updateSystemTheme = (function() { + if (!window.matchMedia) { + // fallback to the CSS computed value + return () => { + const cssTheme = getComputedStyle(document.documentElement) + .getPropertyValue("content"); + + switchTheme( + window.currentTheme, + window.mainTheme, + JSON.parse(cssTheme) || "light", + true + ); + }; + } + + // only listen to (prefers-color-scheme: dark) because light is the default + const mql = window.matchMedia("(prefers-color-scheme: dark)"); + + function handlePreferenceChange(mql) { + const use = theme => { + switchTheme(window.currentTheme, window.mainTheme, theme, true); + }; + // maybe the user has disabled the setting in the meantime! + if (getSettingValue("use-system-theme") !== "false") { + const lightTheme = getSettingValue("preferred-light-theme") || "light"; + const darkTheme = getSettingValue("preferred-dark-theme") || "dark"; + + if (mql.matches) { + use(darkTheme); + } else { + // prefers a light theme, or has no preference + use(lightTheme); + } + // note: we save the theme so that it doesn't suddenly change when + // the user disables "use-system-theme" and reloads the page or + // navigates to another page + } else { + use(getSettingValue("theme")); + } + } + + mql.addListener(handlePreferenceChange); + + return () => { + handlePreferenceChange(mql); + }; +})(); + +function switchToSavedTheme() { + switchTheme( + window.currentTheme, + window.mainTheme, + getSettingValue("theme") || "light", + false + ); +} + +if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { + // update the preferred dark theme if the user is already using a dark theme + // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732 + if (getSettingValue("use-system-theme") === null + && getSettingValue("preferred-dark-theme") === null + && darkThemes.indexOf(localStoredTheme) >= 0) { + updateLocalStorage("preferred-dark-theme", localStoredTheme); + } + + // call the function to initialize the theme at least once! + updateSystemTheme(); +} else { + switchToSavedTheme(); +} + +if (getSettingValue("source-sidebar-show") === "true") { + // At this point in page load, `document.body` is not available yet. + // Set a class on the `<html>` element instead. + addClass(document.documentElement, "source-sidebar-expanded"); +} + +// If we navigate away (for example to a settings page), and then use the back or +// forward button to get back to a page, the theme may have changed in the meantime. +// But scripts may not be re-loaded in such a case due to the bfcache +// (https://web.dev/bfcache/). The "pageshow" event triggers on such navigations. +// Use that opportunity to update the theme. +// We use a setTimeout with a 0 timeout here to put the change on the event queue. +// For some reason, if we try to change the theme while the `pageshow` event is +// running, it sometimes fails to take effect. The problem manifests on Chrome, +// specifically when talking to a remote website with no caching. +window.addEventListener("pageshow", ev => { + if (ev.persisted) { + setTimeout(switchToSavedTheme, 0); + } +}); diff --git a/src/librustdoc/html/static/scrape-examples-help.md b/src/librustdoc/html/static/scrape-examples-help.md new file mode 100644 index 000000000..035b2e18b --- /dev/null +++ b/src/librustdoc/html/static/scrape-examples-help.md @@ -0,0 +1,34 @@ +Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function: + +```rust +// src/lib.rs +pub fn a_func() {} +``` + +And you have an example calling this function: + +```rust +// examples/ex.rs +fn main() { + a_crate::a_func(); +} +``` + +Then this code snippet will be included in the documentation for `a_func`. + +## How to read scraped examples + +Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking ↕ in the top-right. If a file contains multiple instances of an item, you can use the ≺ and ≻ buttons to toggle through each instance. + +If there is more than one file that contains examples, then you should click "More examples" to see these examples. + + +## How Rustdoc scrapes examples + +When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation. + +Rustdoc has a few techniques to ensure this doesn't overwhelm documentation readers, and that it doesn't blow up the page size: + +1. For a given item, a maximum of 5 examples are included in the page. The remaining examples are just links to source code. +2. Only one example is shown by default, and the remaining examples are hidden behind a toggle. +3. For a given file that contains examples, only the item containing the examples will be included in the generated documentation. |