summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /src/librustdoc/html
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/html')
-rw-r--r--src/librustdoc/html/format.rs5
-rw-r--r--src/librustdoc/html/highlight.rs25
-rw-r--r--src/librustdoc/html/markdown.rs665
-rw-r--r--src/librustdoc/html/markdown/tests.rs204
-rw-r--r--src/librustdoc/html/render/context.rs74
-rw-r--r--src/librustdoc/html/render/mod.rs77
-rw-r--r--src/librustdoc/html/render/print_item.rs449
-rw-r--r--src/librustdoc/html/render/search_index.rs491
-rw-r--r--src/librustdoc/html/render/sidebar.rs46
-rw-r--r--src/librustdoc/html/sources.rs2
-rw-r--r--src/librustdoc/html/static/css/noscript.css213
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css512
-rw-r--r--src/librustdoc/html/static/css/settings.css63
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css180
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css101
-rw-r--r--src/librustdoc/html/static/css/themes/light.css98
-rw-r--r--src/librustdoc/html/static/js/externs.js54
-rw-r--r--src/librustdoc/html/static/js/main.js49
-rw-r--r--src/librustdoc/html/static/js/search.js752
-rw-r--r--src/librustdoc/html/static/js/settings.js8
-rw-r--r--src/librustdoc/html/static/js/storage.js36
-rw-r--r--src/librustdoc/html/static_files.rs4
-rw-r--r--src/librustdoc/html/templates/page.html16
-rw-r--r--src/librustdoc/html/templates/type_layout.html4
24 files changed, 2780 insertions, 1348 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2f611c31a..2751b6613 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -250,8 +250,7 @@ impl clean::Generics {
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
- let mut real_params =
- self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
+ let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
if real_params.peek().is_none() {
return Ok(());
}
@@ -1599,7 +1598,7 @@ impl PrintWithSpace for hir::Unsafety {
impl PrintWithSpace for hir::IsAsync {
fn print_with_space(&self) -> &str {
match self {
- hir::IsAsync::Async => "async ",
+ hir::IsAsync::Async(_) => "async ",
hir::IsAsync::NotAsync => "",
}
}
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 039e8cdb9..d8e36139a 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -52,8 +52,9 @@ pub(crate) fn render_example_with_highlighting(
out: &mut Buffer,
tooltip: Tooltip,
playground_button: Option<&str>,
+ extra_classes: &[String],
) {
- write_header(out, "rust-example-rendered", None, tooltip);
+ write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
write_code(out, src, None, None);
write_footer(out, playground_button);
}
@@ -65,7 +66,13 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
write!(out, "</pre>");
}
-fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
+fn write_header(
+ out: &mut Buffer,
+ class: &str,
+ extra_content: Option<Buffer>,
+ tooltip: Tooltip,
+ extra_classes: &[String],
+) {
write!(
out,
"<div class=\"example-wrap{}\">",
@@ -100,9 +107,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to
out.push_buffer(extra);
}
if class.is_empty() {
- write!(out, "<pre class=\"rust\">");
+ write!(
+ out,
+ "<pre class=\"rust{}{}\">",
+ if extra_classes.is_empty() { "" } else { " " },
+ extra_classes.join(" "),
+ );
} else {
- write!(out, "<pre class=\"rust {class}\">");
+ write!(
+ out,
+ "<pre class=\"rust {class}{}{}\">",
+ if extra_classes.is_empty() { "" } else { " " },
+ extra_classes.join(" "),
+ );
}
write!(out, "<code>");
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 98cc38a10..d24e6e5fa 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -20,12 +20,14 @@
//! edition: Edition::Edition2015,
//! playground: &None,
//! heading_offset: HeadingOffset::H2,
+//! custom_code_classes_in_docs: true,
//! };
//! let html = md.into_string();
//! // ... something using html
//! ```
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
pub(crate) use rustc_resolve::rustdoc::main_body_opts;
@@ -37,8 +39,9 @@ use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::Write;
+use std::iter::Peekable;
use std::ops::{ControlFlow, Range};
-use std::str;
+use std::str::{self, CharIndices};
use crate::clean::RenderedLink;
use crate::doctest;
@@ -93,6 +96,8 @@ pub struct Markdown<'a> {
/// Offset at which we render headings.
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
pub heading_offset: HeadingOffset,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub custom_code_classes_in_docs: bool,
}
/// A struct like `Markdown` that renders the markdown with a table of contents.
pub(crate) struct MarkdownWithToc<'a> {
@@ -101,6 +106,8 @@ pub(crate) struct MarkdownWithToc<'a> {
pub(crate) error_codes: ErrorCodes,
pub(crate) edition: Edition,
pub(crate) playground: &'a Option<Playground>,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub(crate) custom_code_classes_in_docs: bool,
}
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
/// and includes no paragraph tags.
@@ -201,6 +208,7 @@ struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
// Information about the playground if a URL has been specified, containing an
// optional crate name and the URL.
playground: &'p Option<Playground>,
+ custom_code_classes_in_docs: bool,
}
impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
@@ -209,8 +217,15 @@ impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
error_codes: ErrorCodes,
edition: Edition,
playground: &'p Option<Playground>,
+ custom_code_classes_in_docs: bool,
) -> Self {
- CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground }
+ CodeBlocks {
+ inner: iter,
+ check_error_codes: error_codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ }
}
}
@@ -240,14 +255,28 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
let parse_result = match kind {
CodeBlockKind::Fenced(ref lang) => {
- let parse_result =
- LangString::parse_without_check(lang, self.check_error_codes, false);
+ let parse_result = LangString::parse_without_check(
+ lang,
+ self.check_error_codes,
+ false,
+ self.custom_code_classes_in_docs,
+ );
if !parse_result.rust {
+ let added_classes = parse_result.added_classes;
+ let lang_string = if let Some(lang) = parse_result.unknown.first() {
+ format!("language-{}", lang)
+ } else {
+ String::new()
+ };
+ let whitespace = if added_classes.is_empty() { "" } else { " " };
return Some(Event::Html(
format!(
"<div class=\"example-wrap\">\
- <pre class=\"language-{lang}\"><code>{text}</code></pre>\
+ <pre class=\"{lang_string}{whitespace}{added_classes}\">\
+ <code>{text}</code>\
+ </pre>\
</div>",
+ added_classes = added_classes.join(" "),
text = Escape(&original_text),
)
.into(),
@@ -258,6 +287,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
CodeBlockKind::Indented => Default::default(),
};
+ let added_classes = parse_result.added_classes;
let lines = original_text.lines().filter_map(|l| map_line(l).for_html());
let text = lines.intersperse("\n".into()).collect::<String>();
@@ -315,6 +345,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
&mut s,
tooltip,
playground_button.as_deref(),
+ &added_classes,
);
Some(Event::Html(s.into_inner().into()))
}
@@ -711,6 +742,27 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
error_codes: ErrorCodes,
enable_per_target_ignores: bool,
extra_info: Option<&ExtraInfo<'_>>,
+ custom_code_classes_in_docs: bool,
+) {
+ find_codes(
+ doc,
+ tests,
+ error_codes,
+ enable_per_target_ignores,
+ extra_info,
+ false,
+ custom_code_classes_in_docs,
+ )
+}
+
+pub(crate) fn find_codes<T: doctest::Tester>(
+ doc: &str,
+ tests: &mut T,
+ error_codes: ErrorCodes,
+ enable_per_target_ignores: bool,
+ extra_info: Option<&ExtraInfo<'_>>,
+ include_non_rust: bool,
+ custom_code_classes_in_docs: bool,
) {
let mut parser = Parser::new(doc).into_offset_iter();
let mut prev_offset = 0;
@@ -729,12 +781,13 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
error_codes,
enable_per_target_ignores,
extra_info,
+ custom_code_classes_in_docs,
)
}
}
CodeBlockKind::Indented => Default::default(),
};
- if !block_info.rust {
+ if !include_non_rust && !block_info.rust {
continue;
}
@@ -784,7 +837,23 @@ impl<'tcx> ExtraInfo<'tcx> {
ExtraInfo { def_id, sp, tcx }
}
- fn error_invalid_codeblock_attr(&self, msg: String, help: &'static str) {
+ fn error_invalid_codeblock_attr(&self, msg: impl Into<DiagnosticMessage>) {
+ if let Some(def_id) = self.def_id.as_local() {
+ self.tcx.struct_span_lint_hir(
+ crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+ self.tcx.hir().local_def_id_to_hir_id(def_id),
+ self.sp,
+ msg,
+ |l| l,
+ );
+ }
+ }
+
+ fn error_invalid_codeblock_attr_with_help(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ help: impl Into<SubdiagnosticMessage>,
+ ) {
if let Some(def_id) = self.def_id.as_local() {
self.tcx.struct_span_lint_hir(
crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
@@ -799,7 +868,7 @@ impl<'tcx> ExtraInfo<'tcx> {
#[derive(Eq, PartialEq, Clone, Debug)]
pub(crate) struct LangString {
- original: String,
+ pub(crate) original: String,
pub(crate) should_panic: bool,
pub(crate) no_run: bool,
pub(crate) ignore: Ignore,
@@ -808,6 +877,8 @@ pub(crate) struct LangString {
pub(crate) compile_fail: bool,
pub(crate) error_codes: Vec<String>,
pub(crate) edition: Option<Edition>,
+ pub(crate) added_classes: Vec<String>,
+ pub(crate) unknown: Vec<String>,
}
#[derive(Eq, PartialEq, Clone, Debug)]
@@ -817,6 +888,317 @@ pub(crate) enum Ignore {
Some(Vec<String>),
}
+/// This is the parser for fenced codeblocks attributes. It implements the following eBNF:
+///
+/// ```eBNF
+/// lang-string = *(token-list / delimited-attribute-list / comment)
+///
+/// bareword = LEADINGCHAR *(CHAR)
+/// bareword-without-leading-char = CHAR *(CHAR)
+/// quoted-string = QUOTE *(NONQUOTE) QUOTE
+/// token = bareword / quoted-string
+/// token-without-leading-char = bareword-without-leading-char / quoted-string
+/// sep = COMMA/WS *(COMMA/WS)
+/// attribute = (DOT token)/(token EQUAL token-without-leading-char)
+/// attribute-list = [sep] attribute *(sep attribute) [sep]
+/// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
+/// token-list = [sep] token *(sep token) [sep]
+/// comment = OPEN_PAREN *(all characters) CLOSE_PAREN
+///
+/// OPEN_PAREN = "("
+/// CLOSE_PARENT = ")"
+/// OPEN-CURLY-BRACKET = "{"
+/// CLOSE-CURLY-BRACKET = "}"
+/// LEADINGCHAR = ALPHA | DIGIT | "_" | "-" | ":"
+/// ; All ASCII punctuation except comma, quote, equals, backslash, grave (backquote) and braces.
+/// ; Comma is used to separate language tokens, so it can't be used in one.
+/// ; Quote is used to allow otherwise-disallowed characters in language tokens.
+/// ; Equals is used to make key=value pairs in attribute blocks.
+/// ; Backslash and grave are special Markdown characters.
+/// ; Braces are used to start an attribute block.
+/// CHAR = ALPHA | DIGIT | "_" | "-" | ":" | "." | "!" | "#" | "$" | "%" | "&" | "*" | "+" | "/" |
+/// ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~"
+/// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
+/// COMMA = ","
+/// DOT = "."
+/// EQUAL = "="
+///
+/// ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+/// DIGIT = %x30-39
+/// WS = %x09 / " "
+/// ```
+pub(crate) struct TagIterator<'a, 'tcx> {
+ inner: Peekable<CharIndices<'a>>,
+ data: &'a str,
+ is_in_attribute_block: bool,
+ extra: Option<&'a ExtraInfo<'tcx>>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub(crate) enum LangStringToken<'a> {
+ LangToken(&'a str),
+ ClassAttribute(&'a str),
+ KeyValueAttribute(&'a str, &'a str),
+}
+
+fn is_leading_char(c: char) -> bool {
+ c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
+}
+fn is_bareword_char(c: char) -> bool {
+ is_leading_char(c) || ".!#$%&*+/;<>?@^|~".contains(c)
+}
+fn is_separator(c: char) -> bool {
+ c == ' ' || c == ',' || c == '\t'
+}
+
+struct Indices {
+ start: usize,
+ end: usize,
+}
+
+impl<'a, 'tcx> TagIterator<'a, 'tcx> {
+ pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self {
+ Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra }
+ }
+
+ fn emit_error(&self, err: impl Into<DiagnosticMessage>) {
+ if let Some(extra) = self.extra {
+ extra.error_invalid_codeblock_attr(err);
+ }
+ }
+
+ fn skip_separators(&mut self) -> Option<usize> {
+ while let Some((pos, c)) = self.inner.peek() {
+ if !is_separator(*c) {
+ return Some(*pos);
+ }
+ self.inner.next();
+ }
+ None
+ }
+
+ fn parse_string(&mut self, start: usize) -> Option<Indices> {
+ while let Some((pos, c)) = self.inner.next() {
+ if c == '"' {
+ return Some(Indices { start: start + 1, end: pos });
+ }
+ }
+ self.emit_error("unclosed quote string `\"`");
+ None
+ }
+
+ fn parse_class(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+ while let Some((pos, c)) = self.inner.peek().copied() {
+ if is_bareword_char(c) {
+ self.inner.next();
+ } else {
+ let class = &self.data[start + 1..pos];
+ if class.is_empty() {
+ self.emit_error(format!("unexpected `{c}` character after `.`"));
+ return None;
+ } else if self.check_after_token() {
+ return Some(LangStringToken::ClassAttribute(class));
+ } else {
+ return None;
+ }
+ }
+ }
+ let class = &self.data[start + 1..];
+ if class.is_empty() {
+ self.emit_error("missing character after `.`");
+ None
+ } else if self.check_after_token() {
+ Some(LangStringToken::ClassAttribute(class))
+ } else {
+ None
+ }
+ }
+
+ fn parse_token(&mut self, start: usize) -> Option<Indices> {
+ while let Some((pos, c)) = self.inner.peek() {
+ if !is_bareword_char(*c) {
+ return Some(Indices { start, end: *pos });
+ }
+ self.inner.next();
+ }
+ self.emit_error("unexpected end");
+ None
+ }
+
+ fn parse_key_value(&mut self, c: char, start: usize) -> Option<LangStringToken<'a>> {
+ let key_indices =
+ if c == '"' { self.parse_string(start)? } else { self.parse_token(start)? };
+ if key_indices.start == key_indices.end {
+ self.emit_error("unexpected empty string as key");
+ return None;
+ }
+
+ if let Some((_, c)) = self.inner.next() {
+ if c != '=' {
+ self.emit_error(format!("expected `=`, found `{}`", c));
+ return None;
+ }
+ } else {
+ self.emit_error("unexpected end");
+ return None;
+ }
+ let value_indices = match self.inner.next() {
+ Some((pos, '"')) => self.parse_string(pos)?,
+ Some((pos, c)) if is_bareword_char(c) => self.parse_token(pos)?,
+ Some((_, c)) => {
+ self.emit_error(format!("unexpected `{c}` character after `=`"));
+ return None;
+ }
+ None => {
+ self.emit_error("expected value after `=`");
+ return None;
+ }
+ };
+ if value_indices.start == value_indices.end {
+ self.emit_error("unexpected empty string as value");
+ None
+ } else if self.check_after_token() {
+ Some(LangStringToken::KeyValueAttribute(
+ &self.data[key_indices.start..key_indices.end],
+ &self.data[value_indices.start..value_indices.end],
+ ))
+ } else {
+ None
+ }
+ }
+
+ /// Returns `false` if an error was emitted.
+ fn check_after_token(&mut self) -> bool {
+ if let Some((_, c)) = self.inner.peek().copied() {
+ if c == '}' || is_separator(c) || c == '(' {
+ true
+ } else {
+ self.emit_error(format!("unexpected `{c}` character"));
+ false
+ }
+ } else {
+ // The error will be caught on the next iteration.
+ true
+ }
+ }
+
+ fn parse_in_attribute_block(&mut self) -> Option<LangStringToken<'a>> {
+ if let Some((pos, c)) = self.inner.next() {
+ if c == '}' {
+ self.is_in_attribute_block = false;
+ return self.next();
+ } else if c == '.' {
+ return self.parse_class(pos);
+ } else if c == '"' || is_leading_char(c) {
+ return self.parse_key_value(c, pos);
+ } else {
+ self.emit_error(format!("unexpected character `{c}`"));
+ return None;
+ }
+ }
+ self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+ None
+ }
+
+ /// Returns `false` if an error was emitted.
+ fn skip_paren_block(&mut self) -> bool {
+ while let Some((_, c)) = self.inner.next() {
+ if c == ')' {
+ return true;
+ }
+ }
+ self.emit_error("unclosed comment: missing `)` at the end");
+ false
+ }
+
+ fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+ while let Some((pos, c)) = self.inner.next() {
+ if c == '"' {
+ if pos != start {
+ self.emit_error("expected ` `, `{` or `,` found `\"`");
+ return None;
+ }
+ let indices = self.parse_string(pos)?;
+ if let Some((_, c)) = self.inner.peek().copied() &&
+ c != '{' &&
+ !is_separator(c) &&
+ c != '('
+ {
+ self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
+ return None;
+ }
+ return Some(LangStringToken::LangToken(&self.data[indices.start..indices.end]));
+ } else if c == '{' {
+ self.is_in_attribute_block = true;
+ return self.next();
+ } else if is_separator(c) {
+ if pos != start {
+ return Some(LangStringToken::LangToken(&self.data[start..pos]));
+ }
+ return self.next();
+ } else if c == '(' {
+ if !self.skip_paren_block() {
+ return None;
+ }
+ if pos != start {
+ return Some(LangStringToken::LangToken(&self.data[start..pos]));
+ }
+ return self.next();
+ } else if pos == start && is_leading_char(c) {
+ continue;
+ } else if pos != start && is_bareword_char(c) {
+ continue;
+ } else {
+ self.emit_error(format!("unexpected character `{c}`"));
+ return None;
+ }
+ }
+ let token = &self.data[start..];
+ if token.is_empty() { None } else { Some(LangStringToken::LangToken(&self.data[start..])) }
+ }
+}
+
+impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
+ type Item = LangStringToken<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let Some(start) = self.skip_separators() else {
+ if self.is_in_attribute_block {
+ self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+ }
+ return None;
+ };
+ if self.is_in_attribute_block {
+ self.parse_in_attribute_block()
+ } else {
+ self.parse_outside_attribute_block(start)
+ }
+ }
+}
+
+fn tokens(string: &str) -> impl Iterator<Item = LangStringToken<'_>> {
+ // Pandoc, which Rust once used for generating documentation,
+ // expects lang strings to be surrounded by `{}` and for each token
+ // to be proceeded by a `.`. Since some of these lang strings are still
+ // loose in the wild, we strip a pair of surrounding `{}` from the lang
+ // string and a leading `.` from each token.
+
+ let string = string.trim();
+
+ let first = string.chars().next();
+ let last = string.chars().last();
+
+ let string =
+ if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string };
+
+ string
+ .split(|c| c == ',' || c == ' ' || c == '\t')
+ .map(str::trim)
+ .map(|token| token.strip_prefix('.').unwrap_or(token))
+ .filter(|token| !token.is_empty())
+ .map(|token| LangStringToken::LangToken(token))
+}
+
impl Default for LangString {
fn default() -> Self {
Self {
@@ -829,6 +1211,8 @@ impl Default for LangString {
compile_fail: false,
error_codes: Vec::new(),
edition: None,
+ added_classes: Vec::new(),
+ unknown: Vec::new(),
}
}
}
@@ -838,33 +1222,15 @@ impl LangString {
string: &str,
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
- ) -> LangString {
- Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
- }
-
- fn tokens(string: &str) -> impl Iterator<Item = &str> {
- // Pandoc, which Rust once used for generating documentation,
- // expects lang strings to be surrounded by `{}` and for each token
- // to be proceeded by a `.`. Since some of these lang strings are still
- // loose in the wild, we strip a pair of surrounding `{}` from the lang
- // string and a leading `.` from each token.
-
- let string = string.trim();
-
- let first = string.chars().next();
- let last = string.chars().last();
-
- let string = if first == Some('{') && last == Some('}') {
- &string[1..string.len() - 1]
- } else {
- string
- };
-
- string
- .split(|c| c == ',' || c == ' ' || c == '\t')
- .map(str::trim)
- .map(|token| token.strip_prefix('.').unwrap_or(token))
- .filter(|token| !token.is_empty())
+ custom_code_classes_in_docs: bool,
+ ) -> Self {
+ Self::parse(
+ string,
+ allow_error_code_check,
+ enable_per_target_ignores,
+ None,
+ custom_code_classes_in_docs,
+ )
}
fn parse(
@@ -872,102 +1238,141 @@ impl LangString {
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
extra: Option<&ExtraInfo<'_>>,
- ) -> LangString {
+ custom_code_classes_in_docs: bool,
+ ) -> Self {
let allow_error_code_check = allow_error_code_check.as_bool();
let mut seen_rust_tags = false;
let mut seen_other_tags = false;
+ let mut seen_custom_tag = false;
let mut data = LangString::default();
let mut ignores = vec![];
data.original = string.to_owned();
- for token in Self::tokens(string) {
- match token {
- "should_panic" => {
- data.should_panic = true;
- seen_rust_tags = !seen_other_tags;
- }
- "no_run" => {
- data.no_run = true;
- seen_rust_tags = !seen_other_tags;
- }
- "ignore" => {
- data.ignore = Ignore::All;
- seen_rust_tags = !seen_other_tags;
- }
- x if x.starts_with("ignore-") => {
- if enable_per_target_ignores {
- ignores.push(x.trim_start_matches("ignore-").to_owned());
+ let mut call = |tokens: &mut dyn Iterator<Item = LangStringToken<'_>>| {
+ for token in tokens {
+ match token {
+ LangStringToken::LangToken("should_panic") => {
+ data.should_panic = true;
seen_rust_tags = !seen_other_tags;
}
- }
- "rust" => {
- data.rust = true;
- seen_rust_tags = true;
- }
- "test_harness" => {
- data.test_harness = true;
- seen_rust_tags = !seen_other_tags || seen_rust_tags;
- }
- "compile_fail" => {
- data.compile_fail = true;
- seen_rust_tags = !seen_other_tags || seen_rust_tags;
- data.no_run = true;
- }
- x if x.starts_with("edition") => {
- data.edition = x[7..].parse::<Edition>().ok();
- }
- x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => {
- if x[1..].parse::<u32>().is_ok() {
- data.error_codes.push(x.to_owned());
+ LangStringToken::LangToken("no_run") => {
+ data.no_run = true;
+ seen_rust_tags = !seen_other_tags;
+ }
+ LangStringToken::LangToken("ignore") => {
+ data.ignore = Ignore::All;
+ seen_rust_tags = !seen_other_tags;
+ }
+ LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
+ if enable_per_target_ignores {
+ ignores.push(x.trim_start_matches("ignore-").to_owned());
+ seen_rust_tags = !seen_other_tags;
+ }
+ }
+ LangStringToken::LangToken("rust") => {
+ data.rust = true;
+ seen_rust_tags = true;
+ }
+ LangStringToken::LangToken("custom") => {
+ if custom_code_classes_in_docs {
+ seen_custom_tag = true;
+ } else {
+ seen_other_tags = true;
+ }
+ }
+ LangStringToken::LangToken("test_harness") => {
+ data.test_harness = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
- } else {
- seen_other_tags = true;
}
- }
- x if extra.is_some() => {
- let s = x.to_lowercase();
- if let Some((flag, help)) = if s == "compile-fail"
- || s == "compile_fail"
- || s == "compilefail"
+ LangStringToken::LangToken("compile_fail") => {
+ data.compile_fail = true;
+ seen_rust_tags = !seen_other_tags || seen_rust_tags;
+ data.no_run = true;
+ }
+ LangStringToken::LangToken(x) if x.starts_with("edition") => {
+ data.edition = x[7..].parse::<Edition>().ok();
+ }
+ LangStringToken::LangToken(x)
+ if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
{
- Some((
- "compile_fail",
- "the code block will either not be tested if not marked as a rust one \
- or won't fail if it compiles successfully",
- ))
- } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
- Some((
- "should_panic",
- "the code block will either not be tested if not marked as a rust one \
- or won't fail if it doesn't panic when running",
- ))
- } else if s == "no-run" || s == "no_run" || s == "norun" {
- Some((
- "no_run",
- "the code block will either not be tested if not marked as a rust one \
- or will be run (which you might not want)",
- ))
- } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
- Some((
- "test_harness",
- "the code block will either not be tested if not marked as a rust one \
- or the code will be wrapped inside a main function",
- ))
- } else {
- None
- } {
- if let Some(extra) = extra {
- extra.error_invalid_codeblock_attr(
- format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
- help,
- );
+ if x[1..].parse::<u32>().is_ok() {
+ data.error_codes.push(x.to_owned());
+ seen_rust_tags = !seen_other_tags || seen_rust_tags;
+ } else {
+ seen_other_tags = true;
}
}
- seen_other_tags = true;
+ LangStringToken::LangToken(x) if extra.is_some() => {
+ let s = x.to_lowercase();
+ if let Some((flag, help)) = if s == "compile-fail"
+ || s == "compile_fail"
+ || s == "compilefail"
+ {
+ Some((
+ "compile_fail",
+ "the code block will either not be tested if not marked as a rust one \
+ or won't fail if it compiles successfully",
+ ))
+ } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
+ Some((
+ "should_panic",
+ "the code block will either not be tested if not marked as a rust one \
+ or won't fail if it doesn't panic when running",
+ ))
+ } else if s == "no-run" || s == "no_run" || s == "norun" {
+ Some((
+ "no_run",
+ "the code block will either not be tested if not marked as a rust one \
+ or will be run (which you might not want)",
+ ))
+ } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
+ Some((
+ "test_harness",
+ "the code block will either not be tested if not marked as a rust one \
+ or the code will be wrapped inside a main function",
+ ))
+ } else {
+ None
+ } {
+ if let Some(extra) = extra {
+ extra.error_invalid_codeblock_attr_with_help(
+ format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
+ help,
+ );
+ }
+ }
+ seen_other_tags = true;
+ data.unknown.push(x.to_owned());
+ }
+ LangStringToken::LangToken(x) => {
+ seen_other_tags = true;
+ data.unknown.push(x.to_owned());
+ }
+ LangStringToken::KeyValueAttribute(key, value) => {
+ if custom_code_classes_in_docs {
+ if key == "class" {
+ data.added_classes.push(value.to_owned());
+ } else if let Some(extra) = extra {
+ extra.error_invalid_codeblock_attr(format!(
+ "unsupported attribute `{key}`"
+ ));
+ }
+ } else {
+ seen_other_tags = true;
+ }
+ }
+ LangStringToken::ClassAttribute(class) => {
+ data.added_classes.push(class.to_owned());
+ }
}
- _ => seen_other_tags = true,
}
+ };
+
+ if custom_code_classes_in_docs {
+ call(&mut TagIterator::new(string, extra).into_iter())
+ } else {
+ call(&mut tokens(string))
}
// ignore-foo overrides ignore
@@ -975,7 +1380,7 @@ impl LangString {
data.ignore = Ignore::Some(ignores);
}
- data.rust &= !seen_other_tags || seen_rust_tags;
+ data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags);
data
}
@@ -991,6 +1396,7 @@ impl Markdown<'_> {
edition,
playground,
heading_offset,
+ custom_code_classes_in_docs,
} = self;
// This is actually common enough to special-case
@@ -1013,7 +1419,7 @@ impl Markdown<'_> {
let p = Footnotes::new(p);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = TableWrapper::new(p);
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
s
@@ -1022,7 +1428,14 @@ impl Markdown<'_> {
impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
- let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
+ let MarkdownWithToc {
+ content: md,
+ ids,
+ error_codes: codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ } = self;
let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
@@ -1034,7 +1447,7 @@ impl MarkdownWithToc<'_> {
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
let p = Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
}
@@ -1475,7 +1888,11 @@ pub(crate) struct RustCodeBlock {
/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
/// untagged (and assumed to be rust).
-pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeBlock> {
+pub(crate) fn rust_code_blocks(
+ md: &str,
+ extra_info: &ExtraInfo<'_>,
+ custom_code_classes_in_docs: bool,
+) -> Vec<RustCodeBlock> {
let mut code_blocks = vec![];
if md.is_empty() {
@@ -1492,7 +1909,13 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
let lang_string = if syntax.is_empty() {
Default::default()
} else {
- LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info))
+ LangString::parse(
+ &*syntax,
+ ErrorCodes::Yes,
+ false,
+ Some(extra_info),
+ custom_code_classes_in_docs,
+ )
};
if !lang_string.rust {
continue;
@@ -1574,7 +1997,6 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("crate-search-div".into(), 1);
// This is the list of IDs used in HTML generated in Rust (including the ones
// used in tera template files).
- map.insert("mainThemeStyle".into(), 1);
map.insert("themeStyle".into(), 1);
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
@@ -1610,6 +2032,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("blanket-implementations-list".into(), 1);
map.insert("deref-methods".into(), 1);
map.insert("layout".into(), 1);
+ map.insert("aliased-type".into(), 1);
map
}
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index db8504d15..5eba1d060 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -1,5 +1,8 @@
use super::{find_testable_code, plain_text_summary, short_markdown_summary};
-use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo};
+use super::{
+ ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, LangStringToken, Markdown,
+ MarkdownItemInfo, TagIterator,
+};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
#[test]
@@ -46,15 +49,37 @@ fn test_unique_id() {
fn test_lang_string_parse() {
fn t(lg: LangString) {
let s = &lg.original;
- assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg)
+ assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None, true), lg)
}
t(Default::default());
t(LangString { original: "rust".into(), ..Default::default() });
- t(LangString { original: ".rust".into(), ..Default::default() });
- t(LangString { original: "{rust}".into(), ..Default::default() });
- t(LangString { original: "{.rust}".into(), ..Default::default() });
- t(LangString { original: "sh".into(), rust: false, ..Default::default() });
+ t(LangString {
+ original: "rusta".into(),
+ rust: false,
+ unknown: vec!["rusta".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: "{rust}".into(), rust: true, ..Default::default() });
+ t(LangString {
+ original: "{.rust}".into(),
+ rust: true,
+ added_classes: vec!["rust".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{.rust}".into(),
+ rust: false,
+ added_classes: vec!["rust".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "sh".into(),
+ rust: false,
+ unknown: vec!["sh".into()],
+ ..Default::default()
+ });
t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() });
t(LangString {
original: "ignore-foo".into(),
@@ -70,41 +95,56 @@ fn test_lang_string_parse() {
compile_fail: true,
..Default::default()
});
- t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() });
+ t(LangString {
+ original: "no_run,example".into(),
+ no_run: true,
+ unknown: vec!["example".into()],
+ ..Default::default()
+ });
t(LangString {
original: "sh,should_panic".into(),
should_panic: true,
rust: false,
+ unknown: vec!["sh".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "example,rust".into(),
+ unknown: vec!["example".into()],
..Default::default()
});
- t(LangString { original: "example,rust".into(), ..Default::default() });
t(LangString {
- original: "test_harness,.rust".into(),
+ original: "test_harness,rusta".into(),
test_harness: true,
+ unknown: vec!["rusta".into()],
..Default::default()
});
t(LangString {
original: "text, no_run".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run, ".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run,".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
@@ -117,29 +157,140 @@ fn test_lang_string_parse() {
edition: Some(Edition::Edition2018),
..Default::default()
});
+ t(LangString {
+ original: "{class=test}".into(),
+ added_classes: vec!["test".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=test}".into(),
+ added_classes: vec!["test".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{.test}".into(),
+ added_classes: vec!["test".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{.test}".into(),
+ added_classes: vec!["test".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "rust,{class=test,.test2}".into(),
+ added_classes: vec!["test".into(), "test2".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=test:with:colon .test1}".into(),
+ added_classes: vec!["test:with:colon".into(), "test1".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=test:with:colon .test1}".into(),
+ added_classes: vec!["test:with:colon".into(), "test1".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first,class=second}".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=first,class=second}".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first,.second},unknown".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ unknown: vec!["unknown".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first .second} unknown".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ unknown: vec!["unknown".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString {
+ original: "{.first.second}".into(),
+ rust: true,
+ added_classes: vec!["first.second".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
+ // error
+ t(LangString {
+ original: "{class=first.second}".into(),
+ rust: true,
+ added_classes: vec!["first.second".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString {
+ original: "{class=.first}".into(),
+ added_classes: vec![".first".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: r#"{class="first"}"#.into(),
+ added_classes: vec!["first".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: r#"custom,{class="first"}"#.into(),
+ added_classes: vec!["first".into()],
+ rust: false,
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() });
}
#[test]
fn test_lang_string_tokenizer() {
- fn case(lang_string: &str, want: &[&str]) {
- let have = LangString::tokens(lang_string).collect::<Vec<&str>>();
+ fn case(lang_string: &str, want: &[LangStringToken<'_>]) {
+ let have = TagIterator::new(lang_string, None).collect::<Vec<_>>();
assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string);
}
case("", &[]);
- case("foo", &["foo"]);
- case("foo,bar", &["foo", "bar"]);
- case(".foo,.bar", &["foo", "bar"]);
- case("{.foo,.bar}", &["foo", "bar"]);
- case(" {.foo,.bar} ", &["foo", "bar"]);
- case("foo bar", &["foo", "bar"]);
- case("foo\tbar", &["foo", "bar"]);
- case("foo\t, bar", &["foo", "bar"]);
- case(" foo , bar ", &["foo", "bar"]);
- case(",,foo,,bar,,", &["foo", "bar"]);
- case("foo=bar", &["foo=bar"]);
- case("a-b-c", &["a-b-c"]);
- case("a_b_c", &["a_b_c"]);
+ case("foo", &[LangStringToken::LangToken("foo")]);
+ case("foo,bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(".foo,.bar", &[]);
+ case(
+ "{.foo,.bar}",
+ &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+ );
+ case(
+ " {.foo,.bar} ",
+ &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+ );
+ case("foo bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo\tbar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo\t, bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(" foo , bar ", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(",,foo,,bar,,", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo=bar", &[]);
+ case("a-b-c", &[LangStringToken::LangToken("a-b-c")]);
+ case("a_b_c", &[LangStringToken::LangToken("a_b_c")]);
}
#[test]
@@ -154,6 +305,7 @@ fn test_header() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
@@ -193,6 +345,7 @@ fn test_header_ids_multiple_blocks() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
@@ -297,7 +450,7 @@ fn test_find_testable_code_line() {
}
}
let mut lines = Vec::<usize>::new();
- find_testable_code(input, &mut lines, ErrorCodes::No, false, None);
+ find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true);
assert_eq!(lines, expect);
}
@@ -322,6 +475,7 @@ fn test_ascii_with_prepending_hashtag() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index d7ff248a9..97714afaa 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,8 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
+use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use rustc_span::{sym, Symbol};
@@ -22,13 +24,13 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::FormatRenderer;
+use crate::formats::{self, FormatRenderer};
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -105,8 +107,8 @@ pub(crate) struct SharedContext<'tcx> {
pub(super) module_sorting: ModuleSorting,
/// Additional CSS files to be added to the generated docs.
pub(crate) style_files: Vec<StylePath>,
- /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
- /// "light-v2.css").
+ /// Suffix to add on resource files (if suffix is "-v2" then "search-index.js" becomes
+ /// "search-index-v2.js").
pub(crate) resource_suffix: String,
/// Optional path string to be used to load static files on output pages. If not set, uses
/// combinations of `../` to reach the documentation root.
@@ -147,6 +149,53 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}
+
+ /// Returns a list of impls on the given type, and, if it's a type alias,
+ /// other types that it aliases.
+ pub(crate) fn all_impls_for_item<'a>(
+ &'a self,
+ it: &clean::Item,
+ did: DefId,
+ ) -> Vec<&'a formats::Impl> {
+ let tcx = self.tcx;
+ let cache = &self.cache;
+ let mut saw_impls = FxHashSet::default();
+ let mut v: Vec<&formats::Impl> = cache
+ .impls
+ .get(&did)
+ .map(Vec::as_slice)
+ .unwrap_or(&[])
+ .iter()
+ .filter(|i| saw_impls.insert(i.def_id()))
+ .collect();
+ if let TypeAliasItem(ait) = &*it.kind &&
+ let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+ let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+ let Some(av) = cache.impls.get(&aliased_type_defid) &&
+ let Some(alias_def_id) = it.item_id.as_def_id()
+ {
+ // This branch of the compiler compares types structually, but does
+ // not check trait bounds. That's probably fine, since type aliases
+ // don't normally constrain on them anyway.
+ // https://github.com/rust-lang/rust/issues/21903
+ //
+ // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+ // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+ let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+ let reject_cx = DeepRejectCtxt {
+ treat_obligation_params: TreatParams::AsCandidateKey,
+ };
+ v.extend(av.iter().filter(|impl_| {
+ if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+ reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+ && saw_impls.insert(impl_def_id)
+ } else {
+ false
+ }
+ }));
+ }
+ v
+ }
}
impl<'tcx> Context<'tcx> {
@@ -463,6 +512,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
generate_link_to_definition,
call_locations,
no_emit_shared,
+ html_no_source,
..
} = options;
@@ -488,7 +538,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
scrape_examples_extension: !call_locations.is_empty(),
};
let mut issue_tracker_base_url = None;
- let mut include_sources = true;
+ let mut include_sources = !html_no_source;
// Crawl the crate attributes looking for attributes which control how we're
// going to emit HTML
@@ -664,21 +714,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
You need to enable JavaScript be able to update your settings.\
</section>\
</noscript>\
- <link rel=\"stylesheet\" \
- href=\"{static_root_path}{settings_css}\">\
- <script defer src=\"{static_root_path}{settings_js}\"></script>\
- <link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
- as=\"style\">",
+ <script defer src=\"{static_root_path}{settings_js}\"></script>",
static_root_path = page.get_static_root_path(),
- settings_css = static_files::STATIC_FILES.settings_css,
settings_js = static_files::STATIC_FILES.settings_js,
- theme_light_css = static_files::STATIC_FILES.theme_light_css,
- theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
- theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
);
// Pre-load all theme CSS files, so that switching feels seamless.
//
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index ac9c180a6..3e671a64b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
- pub(crate) parent_idx: Option<usize>,
+ pub(crate) parent_idx: Option<isize>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@ impl Serialize for RenderType {
let id = match &self.id {
// 0 is a sentinel, everything else is one-indexed
None => 0,
- Some(RenderTypeId::Index(idx)) => idx + 1,
+ // concrete type
+ Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+ // generic type parameter
+ Some(RenderTypeId::Index(idx)) => *idx,
_ => panic!("must convert render types to indexes before serializing"),
};
if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@ impl Serialize for RenderType {
pub(crate) enum RenderTypeId {
DefId(DefId),
Primitive(clean::PrimitiveType),
- Index(usize),
+ Index(isize),
}
/// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
pub(crate) struct IndexItemFunctionType {
inputs: Vec<RenderType>,
output: Vec<RenderType>,
+ where_clause: Vec<Vec<RenderType>>,
}
impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
_ => seq.serialize_element(&self.inputs)?,
}
match &self.output[..] {
- [] => {}
+ [] if self.where_clause.is_empty() => {}
[one] if one.generics.is_none() => seq.serialize_element(one)?,
_ => seq.serialize_element(&self.output)?,
}
+ for constraint in &self.where_clause {
+ if let [one] = &constraint[..] && one.generics.is_none() {
+ seq.serialize_element(one)?;
+ } else {
+ seq.serialize_element(constraint)?;
+ }
+ }
seq.end()
}
}
@@ -235,7 +246,7 @@ struct AllTypes {
traits: FxHashSet<ItemEntry>,
macros: FxHashSet<ItemEntry>,
functions: FxHashSet<ItemEntry>,
- typedefs: FxHashSet<ItemEntry>,
+ type_aliases: FxHashSet<ItemEntry>,
opaque_tys: FxHashSet<ItemEntry>,
statics: FxHashSet<ItemEntry>,
constants: FxHashSet<ItemEntry>,
@@ -255,7 +266,7 @@ impl AllTypes {
traits: new_set(100),
macros: new_set(100),
functions: new_set(100),
- typedefs: new_set(100),
+ type_aliases: new_set(100),
opaque_tys: new_set(100),
statics: new_set(100),
constants: new_set(100),
@@ -279,7 +290,7 @@ impl AllTypes {
ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
- ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
+ ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)),
ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
@@ -317,8 +328,8 @@ impl AllTypes {
if !self.functions.is_empty() {
sections.insert(ItemSection::Functions);
}
- if !self.typedefs.is_empty() {
- sections.insert(ItemSection::TypeDefinitions);
+ if !self.type_aliases.is_empty() {
+ sections.insert(ItemSection::TypeAliases);
}
if !self.opaque_tys.is_empty() {
sections.insert(ItemSection::OpaqueTypes);
@@ -374,7 +385,7 @@ impl AllTypes {
print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
print_entries(f, &self.functions, ItemSection::Functions);
- print_entries(f, &self.typedefs, ItemSection::TypeDefinitions);
+ print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes);
print_entries(f, &self.statics, ItemSection::Statics);
@@ -403,7 +414,8 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
- heading_offset: HeadingOffset::H1
+ heading_offset: HeadingOffset::H1,
+ custom_code_classes_in_docs: false,
}
.into_string()
)
@@ -437,6 +449,7 @@ fn render_markdown<'a, 'cx: 'a>(
heading_offset: HeadingOffset,
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(move |f| {
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
f,
"<div class=\"docblock\">{}</div>",
@@ -448,6 +461,7 @@ fn render_markdown<'a, 'cx: 'a>(
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset,
+ custom_code_classes_in_docs,
}
.into_string()
)
@@ -1117,13 +1131,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(it);
+ derefs.insert(did);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
Ok(())
})
}
@@ -1132,15 +1146,17 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- let cache = &shared.cache;
- let Some(v) = cache.impls.get(&it) else { return };
- let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
+ let v = shared.all_impls_for_item(containing_item, did);
+ let v = v.as_slice();
+ let (non_trait, traits): (Vec<&Impl>, _) =
+ v.iter().partition(|i| i.inner_impl().trait_.is_none());
+ let mut saw_impls = FxHashSet::default();
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
@@ -1169,6 +1185,9 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
+ if !saw_impls.insert(i.def_id()) {
+ continue;
+ }
render_impl(
&mut impls_buf,
cx,
@@ -1214,8 +1233,10 @@ fn render_assoc_items_inner(
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, _) =
- concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
+ .into_iter()
+ .filter(|t| saw_impls.insert(t.def_id()))
+ .partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1237,7 +1258,7 @@ fn render_deref_methods(
.iter()
.find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -1771,6 +1792,7 @@ fn render_impl(
</div>",
);
}
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
w,
"<div class=\"docblock\">{}</div>",
@@ -1781,7 +1803,8 @@ fn render_impl(
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
- heading_offset: HeadingOffset::H4
+ heading_offset: HeadingOffset::H4,
+ custom_code_classes_in_docs,
}
.into_string()
);
@@ -2035,7 +2058,7 @@ pub(crate) enum ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2067,7 +2090,7 @@ impl ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2093,7 +2116,7 @@ impl ItemSection {
Self::Unions => "unions",
Self::Enums => "enums",
Self::Functions => "functions",
- Self::TypeDefinitions => "types",
+ Self::TypeAliases => "types",
Self::Statics => "statics",
Self::Constants => "constants",
Self::Traits => "traits",
@@ -2123,7 +2146,7 @@ impl ItemSection {
Self::Unions => "Unions",
Self::Enums => "Enums",
Self::Functions => "Functions",
- Self::TypeDefinitions => "Type Definitions",
+ Self::TypeAliases => "Type Aliases",
Self::Statics => "Statics",
Self::Constants => "Constants",
Self::Traits => "Traits",
@@ -2154,7 +2177,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
ItemType::Union => ItemSection::Unions,
ItemType::Enum => ItemSection::Enums,
ItemType::Function => ItemSection::Functions,
- ItemType::Typedef => ItemSection::TypeDefinitions,
+ ItemType::TypeAlias => ItemSection::TypeAliases,
ItemType::Static => ItemSection::Statics,
ItemType::Constant => ItemSection::Constants,
ItemType::Trait => ItemSection::Traits,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 6cab34986..c6751c958 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -198,7 +198,7 @@ pub(super) fn print_item(
clean::StructItem(..) => "Struct ",
clean::UnionItem(..) => "Union ",
clean::EnumItem(..) => "Enum ",
- clean::TypedefItem(..) => "Type Definition ",
+ clean::TypeAliasItem(..) => "Type Alias ",
clean::MacroItem(..) => "Macro ",
clean::ProcMacroItem(ref mac) => match mac.kind {
MacroKind::Bang => "Macro ",
@@ -273,7 +273,7 @@ pub(super) fn print_item(
clean::StructItem(ref s) => item_struct(buf, cx, item, s),
clean::UnionItem(ref s) => item_union(buf, cx, item, s),
clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
- clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t),
+ clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t),
clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
@@ -343,7 +343,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
ItemType::Static => 8,
ItemType::Trait => 9,
ItemType::Function => 10,
- ItemType::Typedef => 12,
+ ItemType::TypeAlias => 12,
ItemType::Union => 13,
_ => 14 + ty as u8,
}
@@ -1217,8 +1217,8 @@ fn item_opaque_ty(
.unwrap();
}
-fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
+ fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
wrap_item(w, |w| {
write!(
w,
@@ -1237,6 +1237,75 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if let Some(inner_type) = &t.inner_type {
+ write!(
+ w,
+ "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+ Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
+ );
+
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+ let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ wrap_item(w, |w| {
+ let variants_len = variants.len();
+ let variants_count = variants_iter().count();
+ let has_stripped_entries = variants_len != variants_count;
+
+ write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_enum_fields(
+ w,
+ cx,
+ Some(&t.generics),
+ variants_iter(),
+ variants_count,
+ has_stripped_entries,
+ *is_non_exhaustive,
+ )
+ });
+ item_variants(w, cx, it, variants_iter());
+ }
+ clean::TypeAliasInnerType::Union { fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ None,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ *ctor_kind,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ }
+ }
+
let def_id = it.item_id.expect_def_id();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
@@ -1315,6 +1384,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
s: &'a [clean::Item],
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(|f| {
+ if s.iter()
+ .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+ {
+ return f.write_str("/* private fields */");
+ }
+
for (i, ty) in s.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
@@ -1332,7 +1407,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
let tcx = cx.tcx();
let count_variants = e.variants().count();
- wrap_item(w, |mut w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it, tcx);
write!(
w,
@@ -1341,148 +1416,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
- if !print_where_clause_and_check(w, &e.generics, cx) {
- // If there wasn't a `where` clause, we add a whitespace.
- w.write_str(" ");
- }
+ render_enum_fields(
+ w,
+ cx,
+ Some(&e.generics),
+ e.variants(),
+ count_variants,
+ e.has_stripped_entries(),
+ it.is_non_exhaustive(),
+ );
+ });
- let variants_stripped = e.has_stripped_entries();
- if count_variants == 0 && !variants_stripped {
- w.write_str("{}");
- } else {
- w.write_str("{\n");
- let toggle = should_hide_fields(count_variants);
- if toggle {
- toggle_open(&mut w, format_args!("{count_variants} variants"));
- }
- for v in e.variants() {
- w.write_str(" ");
- let name = v.name.unwrap();
- match *v.kind {
- // FIXME(#101337): Show discriminant
- clean::VariantItem(ref var) => match var.kind {
- clean::VariantKind::CLike => w.write_str(name.as_str()),
- clean::VariantKind::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
- }
- clean::VariantKind::Struct(ref s) => {
- render_struct(w, v, None, None, &s.fields, " ", false, cx);
- }
- },
- _ => unreachable!(),
- }
- w.write_str(",\n");
- }
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- if variants_stripped && !it.is_non_exhaustive() {
- w.write_str(" // some variants omitted\n");
- }
- if toggle {
- toggle_close(&mut w);
+ if count_variants != 0 {
+ item_variants(w, cx, it, e.variants());
+ }
+ let def_id = it.item_id.expect_def_id();
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn render_enum_fields<'a>(
+ mut w: &mut Buffer,
+ cx: &mut Context<'_>,
+ g: Option<&clean::Generics>,
+ variants: impl Iterator<Item = &'a clean::Item>,
+ count_variants: usize,
+ has_stripped_entries: bool,
+ is_non_exhaustive: bool,
+) {
+ if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
+ // If there wasn't a `where` clause, we add a whitespace.
+ w.write_str(" ");
+ }
+
+ let variants_stripped = has_stripped_entries;
+ if count_variants == 0 && !variants_stripped {
+ w.write_str("{}");
+ } else {
+ w.write_str("{\n");
+ let toggle = should_hide_fields(count_variants);
+ if toggle {
+ toggle_open(&mut w, format_args!("{count_variants} variants"));
+ }
+ const TAB: &str = " ";
+ for v in variants {
+ w.write_str(TAB);
+ let name = v.name.unwrap();
+ match *v.kind {
+ // FIXME(#101337): Show discriminant
+ clean::VariantItem(ref var) => match var.kind {
+ clean::VariantKind::CLike => w.write_str(name.as_str()),
+ clean::VariantKind::Tuple(ref s) => {
+ write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ }
+ clean::VariantKind::Struct(ref s) => {
+ render_struct(w, v, None, None, &s.fields, TAB, false, cx);
+ }
+ },
+ _ => unreachable!(),
}
- w.write_str("}");
+ w.write_str(",\n");
}
- });
- write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if variants_stripped && !is_non_exhaustive {
+ w.write_str(" // some variants omitted\n");
+ }
+ if toggle {
+ toggle_close(&mut w);
+ }
+ w.write_str("}");
+ }
+}
- if count_variants != 0 {
+fn item_variants<'a>(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ variants: impl Iterator<Item = &'a clean::Item>,
+) {
+ let tcx = cx.tcx();
+ write!(
+ w,
+ "<h2 id=\"variants\" class=\"variants small-section-header\">\
+ Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
+ </h2>\
+ {}\
+ <div class=\"variants\">",
+ document_non_exhaustive_header(it),
+ document_non_exhaustive(it)
+ );
+ for variant in variants {
+ let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
- "<h2 id=\"variants\" class=\"variants small-section-header\">\
- Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
- </h2>\
- {}\
- <div class=\"variants\">",
- document_non_exhaustive_header(it),
- document_non_exhaustive(it)
+ "<section id=\"{id}\" class=\"variant\">\
+ <a href=\"#{id}\" class=\"anchor\">§</a>",
);
- for variant in e.variants() {
- let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<section id=\"{id}\" class=\"variant\">\
- <a href=\"#{id}\" class=\"anchor\">§</a>",
- );
- render_stability_since_raw_with_extra(
- w,
- variant.stable_since(tcx),
- variant.const_stability(tcx),
- it.stable_since(tcx),
- it.const_stable_since(tcx),
- " rightside",
- );
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ render_stability_since_raw_with_extra(
+ w,
+ variant.stable_since(tcx),
+ variant.const_stability(tcx),
+ it.stable_since(tcx),
+ it.const_stable_since(tcx),
+ " rightside",
+ );
+ write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
- let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
+ let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
- if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
- write!(w, "({})", print_tuple_struct_fields(cx, s));
- }
- w.write_str("</h3></section>");
-
- let heading_and_fields = match &variant_data.kind {
- clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
- clean::VariantKind::Tuple(fields) => {
- // Documentation on tuple variant fields is rare, so to reduce noise we only emit
- // the section if at least one field is documented.
- if fields.iter().any(|f| !f.doc_value().is_empty()) {
- Some(("Tuple Fields", fields))
- } else {
- None
- }
+ if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
+ write!(w, "({})", print_tuple_struct_fields(cx, s));
+ }
+ w.write_str("</h3></section>");
+
+ let heading_and_fields = match &variant_data.kind {
+ clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+ clean::VariantKind::Tuple(fields) => {
+ // Documentation on tuple variant fields is rare, so to reduce noise we only emit
+ // the section if at least one field is documented.
+ if fields.iter().any(|f| !f.doc_value().is_empty()) {
+ Some(("Tuple Fields", fields))
+ } else {
+ None
}
- clean::VariantKind::CLike => None,
- };
+ }
+ clean::VariantKind::CLike => None,
+ };
- if let Some((heading, fields)) = heading_and_fields {
- let variant_id =
- cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<div class=\"sub-variant\" id=\"{variant_id}\">\
- <h4>{heading}</h4>\
- {}",
- document_non_exhaustive(variant)
- );
- for field in fields {
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(_)) => {}
- clean::StructFieldItem(ref ty) => {
- let id = cx.derive_id(format!(
- "variant.{}.field.{}",
- variant.name.unwrap(),
- field.name.unwrap()
- ));
- write!(
- w,
- "<div class=\"sub-variant-field\">\
+ if let Some((heading, fields)) = heading_and_fields {
+ let variant_id =
+ cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
+ write!(
+ w,
+ "<div class=\"sub-variant\" id=\"{variant_id}\">\
+ <h4>{heading}</h4>\
+ {}",
+ document_non_exhaustive(variant)
+ );
+ for field in fields {
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(_)) => {}
+ clean::StructFieldItem(ref ty) => {
+ let id = cx.derive_id(format!(
+ "variant.{}.field.{}",
+ variant.name.unwrap(),
+ field.name.unwrap()
+ ));
+ write!(
+ w,
+ "<div class=\"sub-variant-field\">\
<span id=\"{id}\" class=\"small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}: {t}</code>\
</span>",
- f = field.name.unwrap(),
- t = ty.print(cx),
- );
- write!(
- w,
- "{}</div>",
- document(cx, field, Some(variant), HeadingOffset::H5)
- );
- }
- _ => unreachable!(),
+ f = field.name.unwrap(),
+ t = ty.print(cx),
+ );
+ write!(
+ w,
+ "{}</div>",
+ document(cx, field, Some(variant), HeadingOffset::H5)
+ );
}
+ _ => unreachable!(),
}
- w.write_str("</div>");
}
-
- write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+ w.write_str("</div>");
}
- write!(w, "</div>");
+
+ write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
}
- let def_id = it.item_id.expect_def_id();
- write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
- write!(w, "{}", document_type_layout(cx, def_id));
+ write!(w, "</div>");
}
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1593,15 +1699,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- let mut fields = s
- .fields
+ item_fields(w, cx, it, &s.fields, s.ctor_kind);
+
+ let def_id = it.item_id.expect_def_id();
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn item_fields(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ fields: &Vec<clean::Item>,
+ ctor_kind: Option<CtorKind>,
+) {
+ let mut fields = fields
.iter()
.filter_map(|f| match *f.kind {
clean::StructFieldItem(ref ty) => Some((f, ty)),
_ => None,
})
.peekable();
- if let None | Some(CtorKind::Fn) = s.ctor_kind {
+ if let None | Some(CtorKind::Fn) = ctor_kind {
if fields.peek().is_some() {
write!(
w,
@@ -1609,7 +1728,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>\
{}",
- if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+ if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
@@ -1630,9 +1749,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
}
}
- let def_id = it.item_id.expect_def_id();
- write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
- write!(w, "{}", document_type_layout(cx, def_id));
}
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1871,7 +1987,7 @@ fn render_union<'a, 'cx: 'a>(
}
fn render_struct(
- mut w: &mut Buffer,
+ w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
ty: Option<CtorKind>,
@@ -1891,6 +2007,29 @@ fn render_struct(
if let Some(g) = g {
write!(w, "{}", g.print(cx))
}
+ render_struct_fields(
+ w,
+ g,
+ ty,
+ fields,
+ tab,
+ structhead,
+ it.has_stripped_entries().unwrap_or(false),
+ cx,
+ )
+}
+
+fn render_struct_fields(
+ mut w: &mut Buffer,
+ g: Option<&clean::Generics>,
+ ty: Option<CtorKind>,
+ fields: &[clean::Item],
+ tab: &str,
+ structhead: bool,
+ has_stripped_entries: bool,
+ cx: &Context<'_>,
+) {
+ let tcx = cx.tcx();
match ty {
None => {
let where_displayed =
@@ -1922,11 +2061,11 @@ fn render_struct(
}
if has_visible_fields {
- if it.has_stripped_entries().unwrap() {
+ if has_stripped_entries {
write!(w, "\n{tab} /* private fields */");
}
write!(w, "\n{tab}");
- } else if it.has_stripped_entries().unwrap() {
+ } else if has_stripped_entries {
write!(w, " /* private fields */ ");
}
if toggle {
@@ -1936,21 +2075,31 @@ fn render_struct(
}
Some(CtorKind::Fn) => {
w.write_str("(");
- for (i, field) in fields.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
- clean::StructFieldItem(ref ty) => {
- write!(
- w,
- "{}{}",
- visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
- ty.print(cx),
- )
+ if fields.iter().all(|field| {
+ matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+ }) {
+ write!(w, "/* private fields */");
+ } else {
+ for (i, field) in fields.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ");
+ }
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+ clean::StructFieldItem(ref ty) => {
+ write!(
+ w,
+ "{}{}",
+ visibility_print_with_space(
+ field.visibility(tcx),
+ field.item_id,
+ cx
+ ),
+ ty.print(cx),
+ )
+ }
+ _ => unreachable!(),
}
- _ => unreachable!(),
}
}
w.write_str(")");
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index f34be120d..78c443b22 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -1,10 +1,10 @@
use std::collections::hash_map::Entry;
use std::collections::BTreeMap;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
+use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
use crate::clean;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -68,19 +68,19 @@ pub(crate) fn build_index<'tcx>(
// Reduce `DefId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
let mut lastpath = "";
- let mut lastpathid = 0usize;
+ let mut lastpathid = 0isize;
// First, on function signatures
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
for item in search_index.iter_mut() {
fn insert_into_map<F: std::hash::Hash + Eq>(
ty: &mut RenderType,
- map: &mut FxHashMap<F, usize>,
+ map: &mut FxHashMap<F, isize>,
itemid: F,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
- path: Symbol,
+ path: &[Symbol],
) {
match map.entry(itemid) {
Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
@@ -88,7 +88,7 @@ pub(crate) fn build_index<'tcx>(
let pathid = *lastpathid;
entry.insert(pathid);
*lastpathid += 1;
- crate_paths.push((item_type, path));
+ crate_paths.push((item_type, path.to_vec()));
ty.id = Some(RenderTypeId::Index(pathid));
}
}
@@ -97,10 +97,10 @@ pub(crate) fn build_index<'tcx>(
fn convert_render_type(
ty: &mut RenderType,
cache: &mut Cache,
- itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
- primitives: &mut FxHashMap<Symbol, usize>,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+ primitives: &mut FxHashMap<Symbol, isize>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
) {
if let Some(generics) = &mut ty.generics {
for item in generics {
@@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
item_type,
- *fqp.last().unwrap(),
+ fqp,
);
} else {
ty.id = None;
@@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
ItemType::Primitive,
- sym,
+ &[sym],
);
}
RenderTypeId::Index(_) => {}
@@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
&mut crate_paths,
);
}
+ for constraint in &mut search_type.where_clause {
+ for trait_ in &mut constraint[..] {
+ convert_render_type(
+ trait_,
+ cache,
+ &mut itemid_to_pathid,
+ &mut primitives,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ }
}
}
@@ -191,7 +203,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid += 1;
if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, *fqp.last().unwrap()));
+ crate_paths.push((short, fqp.clone()));
Some(pathid)
} else {
None
@@ -213,118 +225,163 @@ pub(crate) fn build_index<'tcx>(
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
- paths: Vec<(ItemType, Symbol)>,
+ paths: Vec<(ItemType, Vec<Symbol>)>,
// The String is alias name and the vec is the list of the elements with this alias.
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
}
+ struct Paths {
+ ty: ItemType,
+ name: Symbol,
+ path: Option<usize>,
+ }
+
+ impl Serialize for Paths {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.ty)?;
+ seq.serialize_element(self.name.as_str())?;
+ if let Some(ref path) = self.path {
+ seq.serialize_element(path)?;
+ }
+ seq.end()
+ }
+ }
+
impl<'a> Serialize for CrateData<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
+ let mut extra_paths = FxHashMap::default();
+ // We need to keep the order of insertion, hence why we use an `IndexMap`. Then we will
+ // insert these "extra paths" (which are paths of items from external crates) into the
+ // `full_paths` list at the end.
+ let mut revert_extra_paths = FxIndexMap::default();
+ let mut mod_paths = FxHashMap::default();
+ for (index, item) in self.items.iter().enumerate() {
+ if item.path.is_empty() {
+ continue;
+ }
+ mod_paths.insert(&item.path, index);
+ }
+ let mut paths = Vec::with_capacity(self.paths.len());
+ for (ty, path) in &self.paths {
+ if path.len() < 2 {
+ paths.push(Paths { ty: *ty, name: path[0], path: None });
+ continue;
+ }
+ let full_path = join_with_double_colon(&path[..path.len() - 1]);
+ if let Some(index) = mod_paths.get(&full_path) {
+ paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
+ continue;
+ }
+ // It means it comes from an external crate so the item and its path will be
+ // stored into another array.
+ //
+ // `index` is put after the last `mod_paths`
+ let index = extra_paths.len() + self.items.len();
+ if !revert_extra_paths.contains_key(&index) {
+ revert_extra_paths.insert(index, full_path.clone());
+ }
+ match extra_paths.entry(full_path) {
+ Entry::Occupied(entry) => {
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(*entry.get()),
+ });
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(index);
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(index),
+ });
+ }
+ }
+ }
+
+ let mut names = Vec::with_capacity(self.items.len());
+ let mut types = String::with_capacity(self.items.len());
+ let mut full_paths = Vec::with_capacity(self.items.len());
+ let mut descriptions = Vec::with_capacity(self.items.len());
+ let mut parents = Vec::with_capacity(self.items.len());
+ let mut functions = Vec::with_capacity(self.items.len());
+ let mut deprecated = Vec::with_capacity(self.items.len());
+
+ for (index, item) in self.items.iter().enumerate() {
+ let n = item.ty as u8;
+ let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
+ assert!(c <= 'z', "item types must fit within ASCII printables");
+ types.push(c);
+
+ assert_eq!(
+ item.parent.is_some(),
+ item.parent_idx.is_some(),
+ "`{}` is missing idx",
+ item.name
+ );
+ // 0 is a sentinel, everything else is one-indexed
+ parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0));
+
+ names.push(item.name.as_str());
+ descriptions.push(&item.desc);
+
+ if !item.path.is_empty() {
+ full_paths.push((index, &item.path));
+ }
+
+ // Fake option to get `0` out as a sentinel instead of `null`.
+ // We want to use `0` because it's three less bytes.
+ enum FunctionOption<'a> {
+ Function(&'a IndexItemFunctionType),
+ None,
+ }
+ impl<'a> Serialize for FunctionOption<'a> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ match self {
+ FunctionOption::None => 0.serialize(serializer),
+ FunctionOption::Function(ty) => ty.serialize(serializer),
+ }
+ }
+ }
+ functions.push(match &item.search_type {
+ Some(ty) => FunctionOption::Function(ty),
+ None => FunctionOption::None,
+ });
+
+ if item.deprecation.is_some() {
+ deprecated.push(index);
+ }
+ }
+
+ for (index, path) in &revert_extra_paths {
+ full_paths.push((*index, path));
+ }
+
let has_aliases = !self.aliases.is_empty();
let mut crate_data =
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
crate_data.serialize_field("doc", &self.doc)?;
- crate_data.serialize_field(
- "t",
- &self
- .items
- .iter()
- .map(|item| {
- let n = item.ty as u8;
- let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
- assert!(c <= 'z', "item types must fit within ASCII printables");
- c
- })
- .collect::<String>(),
- )?;
- crate_data.serialize_field(
- "n",
- &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "q",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of item indices and full paths
- .filter_map(
- |(index, item)| {
- if item.path.is_empty() { None } else { Some((index, &item.path)) }
- },
- )
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "d",
- &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "i",
- &self
- .items
- .iter()
- .map(|item| {
- assert_eq!(
- item.parent.is_some(),
- item.parent_idx.is_some(),
- "`{}` is missing idx",
- item.name
- );
- // 0 is a sentinel, everything else is one-indexed
- item.parent_idx.map(|x| x + 1).unwrap_or(0)
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "f",
- &self
- .items
- .iter()
- .map(|item| {
- // Fake option to get `0` out as a sentinel instead of `null`.
- // We want to use `0` because it's three less bytes.
- enum FunctionOption<'a> {
- Function(&'a IndexItemFunctionType),
- None,
- }
- impl<'a> Serialize for FunctionOption<'a> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- match self {
- FunctionOption::None => 0.serialize(serializer),
- FunctionOption::Function(ty) => ty.serialize(serializer),
- }
- }
- }
- match &item.search_type {
- Some(ty) => FunctionOption::Function(ty),
- None => FunctionOption::None,
- }
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "c",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of deprecated item indices
- .filter_map(|(index, item)| item.deprecation.map(|_| index))
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "p",
- &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
- )?;
+ crate_data.serialize_field("t", &types)?;
+ crate_data.serialize_field("n", &names)?;
+ // Serialize as an array of item indices and full paths
+ crate_data.serialize_field("q", &full_paths)?;
+ crate_data.serialize_field("d", &descriptions)?;
+ crate_data.serialize_field("i", &parents)?;
+ crate_data.serialize_field("f", &functions)?;
+ crate_data.serialize_field("c", &deprecated)?;
+ crate_data.serialize_field("p", &paths)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -357,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
- let (mut inputs, mut output) = match *item.kind {
+ let (mut inputs, mut output, where_clause) = match *item.kind {
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -367,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
inputs.retain(|a| a.id.is_some() || a.generics.is_some());
output.retain(|a| a.id.is_some() || a.generics.is_some());
- Some(IndexItemFunctionType { inputs, output })
+ Some(IndexItemFunctionType { inputs, output, where_clause })
}
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -387,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_id(type_)
}
- // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+ // The type parameters are converted to generics in `simplify_fn_type`
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+ clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
// Not supported yet
clean::BareFunction(_)
| clean::Generic(_)
| clean::ImplTrait(_)
- | clean::Tuple(_)
| clean::QPath { .. }
| clean::Infer => None,
}
}
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+ // other kinds of type parameters are identified by their name
+ Symbol(Symbol),
+ // every argument-position impl trait is its own type parameter
+ Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
self_: Option<&'a Type>,
generics: &Generics,
arg: &'a Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
res: &mut Vec<RenderType>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+ is_return: bool,
cache: &Cache,
) {
- fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
- // generics and impl trait are both identified by their generics,
- // rather than a type name itself
- let anonymous = ty.is_full_generic() || ty.is_impl_trait();
- let generics_empty = generics.is_empty();
-
- if anonymous {
- if generics_empty {
- // This is a type parameter with no trait bounds (for example: `T` in
- // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
- // with an empty type with an empty name. Let's just discard it.
- return;
- } else if generics.len() == 1 {
- // In this case, no need to go through an intermediate state if the type parameter
- // contains only one trait bound.
- //
- // For example:
- //
- // `fn foo<T: Display>(r: Option<T>) {}`
- //
- // In this case, it would contain:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "",
- // generics: [
- // name: "Display",
- // generics: []
- // }]
- // }]
- // }]
- // ```
- //
- // After removing the intermediate (unnecessary) type parameter, it'll become:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "Display",
- // generics: []
- // }]
- // }]
- // ```
- //
- // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
- // need to keep it as is!
- res.push(generics.pop().unwrap());
- return;
- }
- }
- let index_ty = get_index_type(&ty, generics);
- if index_ty.id.is_none() && generics_empty {
- return;
- }
- res.push(index_ty);
- }
-
if recurse >= 10 {
// FIXME: remove this whole recurse thing when the recursion bug is fixed
// See #59502 for the original issue.
@@ -503,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
// for its bounds.
if let Type::Generic(arg_s) = *arg {
// First we check if the bounds are in a `where` predicate...
+ let mut type_bounds = Vec::new();
for where_pred in generics.where_predicates.iter().filter(|g| match g {
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
_ => false,
}) {
- let mut ty_generics = Vec::new();
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
for bound in bounds.iter() {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
}
// Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
- let mut ty_generics = Vec::new();
for bound in bound.get_bounds().unwrap_or(&[]) {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ }
+ if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+ res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+ } else {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
}
} else if let Type::ImplTrait(ref bounds) = *arg {
- let mut ty_generics = Vec::new();
+ let mut type_bounds = Vec::new();
for bound in bounds {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ if is_return && !type_bounds.is_empty() {
+ // In parameter position, `impl Trait` is a unique thing.
+ res.push(RenderType { id: None, generics: Some(type_bounds) });
+ } else {
+ // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ }
} else if let Type::Slice(ref ty) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
} else if let Type::Array(ref ty, _) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
+ } else if let Type::Tuple(ref tys) = *arg {
+ let mut ty_generics = Vec::new();
+ for ty in tys {
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ res.push(get_index_type(arg, ty_generics));
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -594,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
let mut ty_generics = Vec::new();
if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() {
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
gen,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ let id = get_index_type_id(&arg);
+ if id.is_some() || !ty_generics.is_empty() {
+ res.push(RenderType {
+ id,
+ generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+ });
+ }
}
}
@@ -618,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
let decl = &func.decl;
let combined_generics;
@@ -644,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
(None, &func.generics)
};
- let mut all_types = Vec::new();
+ let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+ let mut arg_types = Vec::new();
for arg in decl.inputs.values.iter() {
- let mut args = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
- if !args.is_empty() {
- all_types.extend(args);
- } else {
- all_types.push(get_index_type(&arg.type_, vec![]));
- }
+ simplify_fn_type(
+ self_,
+ generics,
+ &arg.type_,
+ tcx,
+ 0,
+ &mut arg_types,
+ &mut rgen,
+ false,
+ cache,
+ );
}
let mut ret_types = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
- if ret_types.is_empty() {
- ret_types.push(get_index_type(&decl.output, vec![]));
- }
- (all_types, ret_types)
+ simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+ let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+ simplified_params.sort_by_key(|(idx, _)| -idx);
+ (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
}
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index f3da61056..76f63c6f6 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
clean::UnionItem(ref u) => sidebar_union(cx, it, u),
clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
- clean::TypedefItem(_) => sidebar_typedef(cx, it),
+ clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t),
clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
_ => vec![],
@@ -100,7 +100,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_union()
|| it.is_enum()
|| it.is_mod()
- || it.is_typedef()
+ || it.is_type_alias()
{
(
match *it.kind {
@@ -230,8 +230,33 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBl
}
}
-fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+fn sidebar_type_alias<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ t: &'a clean::TypeAlias,
+) -> Vec<LinkBlock<'a>> {
let mut items = vec![];
+ if let Some(inner_type) = &t.inner_type {
+ items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
+ let mut variants = variants
+ .iter()
+ .filter(|i| !i.is_stripped())
+ .filter_map(|v| v.name)
+ .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+ .collect::<Vec<_>>();
+ variants.sort_unstable();
+
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+ }
+ clean::TypeAliasInnerType::Union { fields }
+ | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
+ let fields = get_struct_fields_name(fields);
+ items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+ }
+ }
+ }
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -254,11 +279,12 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let cache = cx.cache();
+ let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
+ let v = v.as_slice();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if let Some(v) = cache.impls.get(&did) {
+ if !v.is_empty() {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -294,7 +320,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v,
+ v.iter().copied(),
&mut derefs,
&mut used_links,
);
@@ -324,7 +350,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: &[Impl],
+ v: impl Iterator<Item = &'a Impl>,
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -334,7 +360,7 @@ fn sidebar_deref_methods<'a>(
if let Some((target, real_target)) =
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -349,7 +375,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
let inner_impl = target
.def_id(c)
.or_else(|| {
@@ -400,7 +426,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls,
+ target_impls.iter(),
derefs,
used_links,
);
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index c4a1ebbec..1d6eafe51 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -134,7 +134,7 @@ impl DocVisitor for SourceCollector<'_, '_> {
let filename = span.filename(sess);
let span = span.inner();
let pos = sess.source_map().lookup_source_file(span.lo());
- let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos);
+ let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_position());
// If it turns out that we couldn't read this file, then we probably
// can't read any of the files (generating html output from json or
// something like that), so just don't include sources for the
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 93aa11a58..fe0cf6dc8 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -28,3 +28,216 @@ nav.sub {
https://github.com/rust-lang/rust/issues/102576 */
display: none;
}
+
+/* Begin: styles for themes
+ Keep the default light and dark themes synchronized with the ones
+ in rustdoc.css */
+
+/* Begin theme: light */
+:root {
+ --main-background-color: white;
+ --main-color: black;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #717171;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
+ --sidebar-background-color: #f5f5f5;
+ --sidebar-background-color-hover: #e0e0e0;
+ --code-block-background-color: #f5f5f5;
+ --scrollbar-track-background-color: #dcdcdc;
+ --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
+ --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
+ --headings-border-bottom-color: #ddd;
+ --border-color: #e0e0e0;
+ --button-background-color: #fff;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --toggle-filter: none;
+ --search-input-focused-border-color: #66afe9;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(35%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --warning-border-color: #ff8e00;
+ --type-link-color: #ad378a;
+ --trait-link-color: #6e4fc9;
+ --assoc-item-link-color: #3873ad;
+ --function-link-color: #ad7c37;
+ --macro-link-color: #068000;
+ --keyword-link-color: #3873ad;
+ --mod-link-color: #3873ad;
+ --link-color: #3873ad;
+ --sidebar-link-color: #356da4;
+ --sidebar-current-link-background-color: #fff;
+ --search-result-link-focus-background-color: #ccc;
+ --search-result-border-color: #aaa3;
+ --search-color: #000;
+ --search-error-code-background-color: #d0cccc;
+ --search-results-alias-color: #000;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #e6e6e6;
+ --search-tab-button-not-selected-background: #e6e6e6;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #fff;
+ --stab-background-color: #fff5d6;
+ --stab-code-color: #000;
+ --code-highlight-kw-color: #8959a8;
+ --code-highlight-kw-2-color: #4271ae;
+ --code-highlight-lifetime-color: #b76514;
+ --code-highlight-prelude-color: #4271ae;
+ --code-highlight-prelude-val-color: #c82829;
+ --code-highlight-number-color: #718c00;
+ --code-highlight-string-color: #718c00;
+ --code-highlight-literal-color: #c82829;
+ --code-highlight-attribute-color: #c82829;
+ --code-highlight-self-color: #c82829;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8e908c;
+ --code-highlight-doc-comment-color: #4d4d4c;
+ --src-line-numbers-span-color: #c67e2d;
+ --src-line-number-highlighted-background-color: #fdffd3;
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: rgb(78, 139, 202);
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: initial;
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+ brightness(114%) contrast(76%);
+ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+ brightness(96%) contrast(93%);
+ --crate-search-hover-border: #717171;
+ --src-sidebar-background-selected: #fff;
+ --src-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #f5f5f5;
+ --codeblock-link-background: #eee;
+ --scrape-example-toggle-line-background: #ccc;
+ --scrape-example-toggle-line-hover-background: #999;
+ --scrape-example-code-line-highlight: #fcffd6;
+ --scrape-example-code-line-highlight-focus: #f6fdb0;
+ --scrape-example-help-border-color: #555;
+ --scrape-example-help-color: #333;
+ --scrape-example-help-hover-border-color: #000;
+ --scrape-example-help-hover-color: #000;
+ --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+ --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+@media (prefers-color-scheme: dark) {
+ /* Begin theme: dark */
+ :root {
+ --main-background-color: #353535;
+ --main-color: #ddd;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #999;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
+ --sidebar-background-color: #505050;
+ --sidebar-background-color-hover: #676767;
+ --code-block-background-color: #2A2A2A;
+ --scrollbar-track-background-color: #717171;
+ --scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
+ --scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
+ --headings-border-bottom-color: #d2d2d2;
+ --border-color: #e0e0e0;
+ --button-background-color: #f0f0f0;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --toggle-filter: invert(100%);
+ --search-input-focused-border-color: #008dfd;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(65%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --warning-border-color: #ff8e00;
+ --type-link-color: #2dbfb8;
+ --trait-link-color: #b78cf2;
+ --assoc-item-link-color: #d2991d;
+ --function-link-color: #2bab63;
+ --macro-link-color: #09bd00;
+ --keyword-link-color: #d2991d;
+ --mod-link-color: #d2991d;
+ --link-color: #d2991d;
+ --sidebar-link-color: #fdbf35;
+ --sidebar-current-link-background-color: #444;
+ --search-result-link-focus-background-color: #616161;
+ --search-result-border-color: #aaa3;
+ --search-color: #111;
+ --search-error-code-background-color: #484848;
+ --search-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #252525;
+ --search-tab-button-not-selected-background: #252525;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #353535;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ab8ac1;
+ --code-highlight-kw-2-color: #769acb;
+ --code-highlight-lifetime-color: #d97f26;
+ --code-highlight-prelude-color: #769acb;
+ --code-highlight-prelude-val-color: #ee6868;
+ --code-highlight-number-color: #83a300;
+ --code-highlight-string-color: #83a300;
+ --code-highlight-literal-color: #ee6868;
+ --code-highlight-attribute-color: #ee6868;
+ --code-highlight-self-color: #ee6868;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8d8d8b;
+ --code-highlight-doc-comment-color: #8ca375;
+ --src-line-numbers-span-color: #3b91e2;
+ --src-line-number-highlighted-background-color: #0a042f;
+ --test-arrow-color: #dedede;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #dedede;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #494a3d;
+ --target-border-color: #bb7410;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+ brightness(90%) contrast(90%);
+ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+ brightness(100%) contrast(91%);
+ --crate-search-hover-border: #2196f3;
+ --src-sidebar-background-selected: #333;
+ --src-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2a2a2a;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+ --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+ }
+/* End theme: dark */
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index b1de8c152..47f9e6502 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -270,7 +270,7 @@ ul ul, ol ul, ul ol, ol ol {
margin-bottom: .625em;
}
-p {
+p, .docblock > .warning {
/* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines.
Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that.
https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */
@@ -278,7 +278,7 @@ p {
}
/* For the last child of a div, the margin will be taken care of
by the margin-top of the next item. */
-p:last-child {
+p:last-child, .docblock > .warning:last-child {
margin: 0;
}
@@ -925,6 +925,70 @@ so that we can apply CSS-filters to change the arrow color in themes */
top: -5px;
}
+.setting-line {
+ margin: 1.2em 0.6em;
+}
+
+.setting-radio input, .setting-check input {
+ margin-right: 0.3em;
+ height: 1.2rem;
+ width: 1.2rem;
+ border: 2px solid var(--settings-input-border-color);
+ outline: none;
+ -webkit-appearance: none;
+ cursor: pointer;
+}
+.setting-radio input {
+ border-radius: 50%;
+}
+
+.setting-radio span, .setting-check span {
+ padding-bottom: 1px;
+}
+
+.setting-radio {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+ min-width: 3.8em;
+ padding: 0.3em;
+ display: inline-flex;
+ align-items: center;
+ cursor: pointer;
+}
+.setting-radio + .setting-radio {
+ margin-left: 0.5em;
+}
+
+.setting-check {
+ margin-right: 20px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+}
+
+.setting-radio input:checked {
+ box-shadow: inset 0 0 0 3px var(--main-background-color);
+ background-color: var(--settings-input-color);
+}
+.setting-check input:checked {
+ background-color: var(--settings-input-color);
+ border-width: 1px;
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
+ <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
+ <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
+}
+.setting-radio input:focus, .setting-check input:focus {
+ box-shadow: 0 0 1px 1px var(--settings-input-color);
+}
+/* In here we combine both `:focus` and `:checked` properties. */
+.setting-radio input:checked:focus {
+ box-shadow: inset 0 0 0 3px var(--main-background-color),
+ 0 0 2px 2px var(--settings-input-color);
+}
+.setting-radio input:hover, .setting-check input:hover {
+ border-color: var(--settings-input-color) !important;
+}
+
/* use larger max-width for help popover, but not for help.html */
#help.popover {
max-width: 600px;
@@ -1096,7 +1160,7 @@ pre.rust .doccomment {
}
.example-wrap.ignore .tooltip {
- color: var(--codeblock-ignore-color);
+ color: var(--codeblock-ignore-color);
}
.example-wrap.compile_fail:hover .tooltip,
@@ -1124,6 +1188,26 @@ pre.rust .doccomment {
font-size: 1.25rem;
}
+/* This class only exists for users who want to draw attention to a particular element in their
+documentation. */
+.content .docblock .warning {
+ border-left: 2px solid var(--warning-border-color);
+ padding: 14px;
+ position: relative;
+ /* The "!important" part is required because the rule is otherwise overruled in this CSS
+ selector: ".docblock > :not(.more-examples-toggle):not(.example-wrap)" */
+ overflow-x: visible !important;
+}
+.content .docblock .warning::before {
+ color: var(--warning-border-color);
+ content: "ⓘ";
+ position: absolute;
+ left: -25px;
+ top: 5px;
+ font-weight: bold;
+ font-size: 1.25rem;
+}
+
a.test-arrow {
visibility: hidden;
position: absolute;
@@ -1271,6 +1355,7 @@ a.tooltip:hover::after {
#search-tabs .count {
font-size: 1rem;
+ font-variant-numeric: tabular-nums;
color: var(--search-tab-title-count-color);
}
@@ -1553,6 +1638,13 @@ However, it's not needed with smaller screen width because the doc/code block is
/* Media Queries */
+/* Make sure all the buttons line wrap at the same time */
+@media (max-width: 850px) {
+ #search-tabs .count {
+ display: block;
+ }
+}
+
/*
WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
If you update this line, then you also need to update the line with the same warning
@@ -1680,10 +1772,6 @@ in src-script.js
display: none !important;
}
- #search-tabs .count {
- display: block;
- }
-
#main-content > details.toggle > summary::before,
#main-content > div > details.toggle > summary::before {
left: -11px;
@@ -2014,3 +2102,413 @@ in src-script.js
}
/* End: styles for --scrape-examples feature */
+
+/* Begin: styles for themes
+
+ Keep the default light and dark themes synchronized with the ones
+ in noscript.css
+
+ The special "Begin theme" and "End theme" below are used by a lot of
+ tooling to ensure different themes all define all the variables. Do not
+ alter their formatting. */
+
+/* Begin theme: light */
+:root[data-theme="light"] {
+ --main-background-color: white;
+ --main-color: black;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #717171;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
+ --sidebar-background-color: #f5f5f5;
+ --sidebar-background-color-hover: #e0e0e0;
+ --code-block-background-color: #f5f5f5;
+ --scrollbar-track-background-color: #dcdcdc;
+ --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
+ --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
+ --headings-border-bottom-color: #ddd;
+ --border-color: #e0e0e0;
+ --button-background-color: #fff;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --toggle-filter: none;
+ --search-input-focused-border-color: #66afe9;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(35%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --warning-border-color: #ff8e00;
+ --type-link-color: #ad378a;
+ --trait-link-color: #6e4fc9;
+ --assoc-item-link-color: #3873ad;
+ --function-link-color: #ad7c37;
+ --macro-link-color: #068000;
+ --keyword-link-color: #3873ad;
+ --mod-link-color: #3873ad;
+ --link-color: #3873ad;
+ --sidebar-link-color: #356da4;
+ --sidebar-current-link-background-color: #fff;
+ --search-result-link-focus-background-color: #ccc;
+ --search-result-border-color: #aaa3;
+ --search-color: #000;
+ --search-error-code-background-color: #d0cccc;
+ --search-results-alias-color: #000;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #e6e6e6;
+ --search-tab-button-not-selected-background: #e6e6e6;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #fff;
+ --stab-background-color: #fff5d6;
+ --stab-code-color: #000;
+ --code-highlight-kw-color: #8959a8;
+ --code-highlight-kw-2-color: #4271ae;
+ --code-highlight-lifetime-color: #b76514;
+ --code-highlight-prelude-color: #4271ae;
+ --code-highlight-prelude-val-color: #c82829;
+ --code-highlight-number-color: #718c00;
+ --code-highlight-string-color: #718c00;
+ --code-highlight-literal-color: #c82829;
+ --code-highlight-attribute-color: #c82829;
+ --code-highlight-self-color: #c82829;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8e908c;
+ --code-highlight-doc-comment-color: #4d4d4c;
+ --src-line-numbers-span-color: #c67e2d;
+ --src-line-number-highlighted-background-color: #fdffd3;
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: rgb(78, 139, 202);
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: initial;
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+ brightness(114%) contrast(76%);
+ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+ brightness(96%) contrast(93%);
+ --crate-search-hover-border: #717171;
+ --src-sidebar-background-selected: #fff;
+ --src-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #f5f5f5;
+ --codeblock-link-background: #eee;
+ --scrape-example-toggle-line-background: #ccc;
+ --scrape-example-toggle-line-hover-background: #999;
+ --scrape-example-code-line-highlight: #fcffd6;
+ --scrape-example-code-line-highlight-focus: #f6fdb0;
+ --scrape-example-help-border-color: #555;
+ --scrape-example-help-color: #333;
+ --scrape-example-help-hover-border-color: #000;
+ --scrape-example-help-hover-color: #000;
+ --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+ --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+/* Begin theme: dark */
+:root[data-theme="dark"] {
+ --main-background-color: #353535;
+ --main-color: #ddd;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #999;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
+ --sidebar-background-color: #505050;
+ --sidebar-background-color-hover: #676767;
+ --code-block-background-color: #2A2A2A;
+ --scrollbar-track-background-color: #717171;
+ --scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
+ --scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
+ --headings-border-bottom-color: #d2d2d2;
+ --border-color: #e0e0e0;
+ --button-background-color: #f0f0f0;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --toggle-filter: invert(100%);
+ --search-input-focused-border-color: #008dfd;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(65%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --warning-border-color: #ff8e00;
+ --type-link-color: #2dbfb8;
+ --trait-link-color: #b78cf2;
+ --assoc-item-link-color: #d2991d;
+ --function-link-color: #2bab63;
+ --macro-link-color: #09bd00;
+ --keyword-link-color: #d2991d;
+ --mod-link-color: #d2991d;
+ --link-color: #d2991d;
+ --sidebar-link-color: #fdbf35;
+ --sidebar-current-link-background-color: #444;
+ --search-result-link-focus-background-color: #616161;
+ --search-result-border-color: #aaa3;
+ --search-color: #111;
+ --search-error-code-background-color: #484848;
+ --search-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #252525;
+ --search-tab-button-not-selected-background: #252525;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #353535;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ab8ac1;
+ --code-highlight-kw-2-color: #769acb;
+ --code-highlight-lifetime-color: #d97f26;
+ --code-highlight-prelude-color: #769acb;
+ --code-highlight-prelude-val-color: #ee6868;
+ --code-highlight-number-color: #83a300;
+ --code-highlight-string-color: #83a300;
+ --code-highlight-literal-color: #ee6868;
+ --code-highlight-attribute-color: #ee6868;
+ --code-highlight-self-color: #ee6868;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8d8d8b;
+ --code-highlight-doc-comment-color: #8ca375;
+ --src-line-numbers-span-color: #3b91e2;
+ --src-line-number-highlighted-background-color: #0a042f;
+ --test-arrow-color: #dedede;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #dedede;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #494a3d;
+ --target-border-color: #bb7410;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+ brightness(90%) contrast(90%);
+ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+ brightness(100%) contrast(91%);
+ --crate-search-hover-border: #2196f3;
+ --src-sidebar-background-selected: #333;
+ --src-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2a2a2a;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+ --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+}
+/* End theme: dark */
+
+/* Begin theme: ayu */
+/*
+Based off of the Ayu theme
+Original by Dempfi (https://github.com/dempfi/ayu)
+*/
+:root[data-theme="ayu"] {
+ --main-background-color: #0f1419;
+ --main-color: #c5c5c5;
+ --settings-input-color: #ffb454;
+ --settings-input-border-color: #999;
+ --settings-button-color: #fff;
+ --settings-button-border-focus: #e0e0e0;
+ --sidebar-background-color: #14191f;
+ --sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
+ --code-block-background-color: #191f26;
+ --scrollbar-track-background-color: transparent;
+ --scrollbar-thumb-background-color: #5c6773;
+ --scrollbar-color: #5c6773 #24292f;
+ --headings-border-bottom-color: #5c6773;
+ --border-color: #5c6773;
+ --button-background-color: #141920;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --toggle-filter: invert(100%);
+ --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
+ --copy-path-button-color: #fff;
+ --copy-path-img-filter: invert(70%);
+ --copy-path-img-hover-filter: invert(100%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --warning-border-color: #ff8e00;
+ --type-link-color: #ffa0a5;
+ --trait-link-color: #39afd7;
+ --assoc-item-link-color: #39afd7;
+ --function-link-color: #fdd687;
+ --macro-link-color: #a37acc;
+ --keyword-link-color: #39afd7;
+ --mod-link-color: #39afd7;
+ --link-color: #39afd7;
+ --sidebar-link-color: #53b1db;
+ --sidebar-current-link-background-color: transparent;
+ --search-result-link-focus-background-color: #3c3c3c;
+ --search-result-border-color: #aaa3;
+ --search-color: #fff;
+ --search-error-code-background-color: #4f4c4c;
+ --search-results-alias-color: #c5c5c5;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: none;
+ --search-tab-button-not-selected-background: transparent !important;
+ --search-tab-button-selected-border-top-color: none;
+ --search-tab-button-selected-background: #141920 !important;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ff7733;
+ --code-highlight-kw-2-color: #ff7733;
+ --code-highlight-lifetime-color: #ff7733;
+ --code-highlight-prelude-color: #69f2df;
+ --code-highlight-prelude-val-color: #ff7733;
+ --code-highlight-number-color: #b8cc52;
+ --code-highlight-string-color: #b8cc52;
+ --code-highlight-literal-color: #ff7733;
+ --code-highlight-attribute-color: #e6e1cf;
+ --code-highlight-self-color: #36a3d9;
+ --code-highlight-macro-color: #a37acc;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #788797;
+ --code-highlight-doc-comment-color: #a1ac88;
+ --src-line-numbers-span-color: #5c6773;
+ --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
+ --test-arrow-color: #788797;
+ --test-arrow-background-color: rgba(57, 175, 215, 0.09);
+ --test-arrow-hover-color: #c5c5c5;
+ --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
+ --target-background-color: rgba(255, 236, 164, 0.06);
+ --target-border-color: rgba(255, 180, 76, 0.85);
+ --kbd-color: #c5c5c5;
+ --kbd-background: #314559;
+ --kbd-box-shadow-color: #5c6773;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
+ brightness(94%) contrast(94%);
+ --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
+ brightness(113%) contrast(76%);
+ --crate-search-hover-border: #e0e0e0;
+ --src-sidebar-background-selected: #14191f;
+ --src-sidebar-background-hover: #14191f;
+ --table-alt-row-background-color: #191f26;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
+ --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
+}
+
+:root[data-theme="ayu"] h1,
+:root[data-theme="ayu"] h2,
+:root[data-theme="ayu"] h3,
+:root[data-theme="ayu"] h4,
+:where(:root[data-theme="ayu"]) h1 a,
+:root[data-theme="ayu"] .sidebar h2 a,
+:root[data-theme="ayu"] .sidebar h3 a,
+:root[data-theme="ayu"] #source-sidebar > .title {
+ color: #fff;
+}
+
+:root[data-theme="ayu"] .docblock code {
+ color: #ffb454;
+}
+
+:root[data-theme="ayu"] .docblock a > code {
+ color: #39AFD7 !important;
+}
+
+:root[data-theme="ayu"] .code-header,
+:root[data-theme="ayu"] .docblock pre > code,
+:root[data-theme="ayu"] pre,
+:root[data-theme="ayu"] pre > code,
+:root[data-theme="ayu"] .item-info code,
+:root[data-theme="ayu"] .rustdoc.source .example-wrap {
+ color: #e6e1cf;
+}
+
+:root[data-theme="ayu"] .sidebar .current,
+:root[data-theme="ayu"] .sidebar a:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:hover,
+:root[data-theme="ayu"] details.dir-entry summary:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:focus,
+:root[data-theme="ayu"] details.dir-entry summary:focus,
+:root[data-theme="ayu"] #src-sidebar div.files > a.selected {
+ color: #ffb44c;
+}
+
+:root[data-theme="ayu"] .sidebar-elems .location {
+ color: #ff7733;
+}
+
+:root[data-theme="ayu"] .src-line-numbers .line-highlighted {
+ color: #708090;
+ padding-right: 7px;
+ border-right: 1px solid #ffb44c;
+}
+
+:root[data-theme="ayu"] .search-results a:hover,
+:root[data-theme="ayu"] .search-results a:focus {
+ color: #fff !important;
+ background-color: #3c3c3c;
+}
+
+:root[data-theme="ayu"] .search-results a {
+ color: #0096cf;
+}
+
+:root[data-theme="ayu"] .search-results a div.desc {
+ color: #c5c5c5;
+}
+
+:root[data-theme="ayu"] .result-name .primitive > i,
+:root[data-theme="ayu"] .result-name .keyword > i {
+ color: #788797;
+}
+
+:root[data-theme="ayu"] #search-tabs > button.selected {
+ border-bottom: 1px solid #ffb44c !important;
+ border-top: none;
+}
+:root[data-theme="ayu"] #search-tabs > button:not(.selected) {
+ border: none;
+ background-color: transparent !important;
+}
+:root[data-theme="ayu"] #search-tabs > button:hover {
+ border-bottom: 1px solid rgba(242, 151, 24, 0.3);
+}
+
+:root[data-theme="ayu"] #settings-menu > a img {
+ filter: invert(100);
+}
+/* End theme: ayu */
+
+/* End: styles for themes */
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
deleted file mode 100644
index c1324c076..000000000
--- a/src/librustdoc/html/static/css/settings.css
+++ /dev/null
@@ -1,63 +0,0 @@
-.setting-line {
- margin: 1.2em 0.6em;
-}
-
-.setting-radio input, .setting-check input {
- margin-right: 0.3em;
- height: 1.2rem;
- width: 1.2rem;
- border: 2px solid var(--settings-input-border-color);
- outline: none;
- -webkit-appearance: none;
- cursor: pointer;
-}
-.setting-radio input {
- border-radius: 50%;
-}
-
-.setting-radio span, .setting-check span {
- padding-bottom: 1px;
-}
-
-.setting-radio {
- margin-top: 0.1em;
- margin-bottom: 0.1em;
- min-width: 3.8em;
- padding: 0.3em;
- display: inline-flex;
- align-items: center;
- cursor: pointer;
-}
-.setting-radio + .setting-radio {
- margin-left: 0.5em;
-}
-
-.setting-check {
- margin-right: 20px;
- display: flex;
- align-items: center;
- cursor: pointer;
-}
-
-.setting-radio input:checked {
- box-shadow: inset 0 0 0 3px var(--main-background-color);
- background-color: var(--settings-input-color);
-}
-.setting-check input:checked {
- background-color: var(--settings-input-color);
- border-width: 1px;
- content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
- <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
- <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
-}
-.setting-radio input:focus, .setting-check input:focus {
- box-shadow: 0 0 1px 1px var(--settings-input-color);
-}
-/* In here we combine both `:focus` and `:checked` properties. */
-.setting-radio input:checked:focus {
- box-shadow: inset 0 0 0 3px var(--main-background-color),
- 0 0 2px 2px var(--settings-input-color);
-}
-.setting-radio input:hover, .setting-check input:hover {
- border-color: var(--settings-input-color) !important;
-}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
deleted file mode 100644
index d8dae51eb..000000000
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
-Based off of the Ayu theme
-Original by Dempfi (https://github.com/dempfi/ayu)
-*/
-
-:root {
- --main-background-color: #0f1419;
- --main-color: #c5c5c5;
- --settings-input-color: #ffb454;
- --settings-input-border-color: #999;
- --settings-button-color: #fff;
- --settings-button-border-focus: #e0e0e0;
- --sidebar-background-color: #14191f;
- --sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
- --code-block-background-color: #191f26;
- --scrollbar-track-background-color: transparent;
- --scrollbar-thumb-background-color: #5c6773;
- --scrollbar-color: #5c6773 #24292f;
- --headings-border-bottom-color: #5c6773;
- --border-color: #5c6773;
- --button-background-color: #141920;
- --right-side-color: grey;
- --code-attribute-color: #999;
- --toggles-color: #999;
- --toggle-filter: invert(100%);
- --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
- --copy-path-button-color: #fff;
- --copy-path-img-filter: invert(70%);
- --copy-path-img-hover-filter: invert(100%);
- --codeblock-error-hover-color: rgb(255, 0, 0);
- --codeblock-error-color: rgba(255, 0, 0, .5);
- --codeblock-ignore-hover-color: rgb(255, 142, 0);
- --codeblock-ignore-color: rgba(255, 142, 0, .6);
- --type-link-color: #ffa0a5;
- --trait-link-color: #39afd7;
- --assoc-item-link-color: #39afd7;
- --function-link-color: #fdd687;
- --macro-link-color: #a37acc;
- --keyword-link-color: #39afd7;
- --mod-link-color: #39afd7;
- --link-color: #39afd7;
- --sidebar-link-color: #53b1db;
- --sidebar-current-link-background-color: transparent;
- --search-result-link-focus-background-color: #3c3c3c;
- --search-result-border-color: #aaa3;
- --search-color: #fff;
- --search-error-code-background-color: #4f4c4c;
- --search-results-alias-color: #c5c5c5;
- --search-results-grey-color: #999;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: none;
- --search-tab-button-not-selected-background: transparent !important;
- --search-tab-button-selected-border-top-color: none;
- --search-tab-button-selected-background: #141920 !important;
- --stab-background-color: #314559;
- --stab-code-color: #e6e1cf;
- --code-highlight-kw-color: #ff7733;
- --code-highlight-kw-2-color: #ff7733;
- --code-highlight-lifetime-color: #ff7733;
- --code-highlight-prelude-color: #69f2df;
- --code-highlight-prelude-val-color: #ff7733;
- --code-highlight-number-color: #b8cc52;
- --code-highlight-string-color: #b8cc52;
- --code-highlight-literal-color: #ff7733;
- --code-highlight-attribute-color: #e6e1cf;
- --code-highlight-self-color: #36a3d9;
- --code-highlight-macro-color: #a37acc;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #788797;
- --code-highlight-doc-comment-color: #a1ac88;
- --src-line-numbers-span-color: #5c6773;
- --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
- --test-arrow-color: #788797;
- --test-arrow-background-color: rgba(57, 175, 215, 0.09);
- --test-arrow-hover-color: #c5c5c5;
- --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
- --target-background-color: rgba(255, 236, 164, 0.06);
- --target-border-color: rgba(255, 180, 76, 0.85);
- --kbd-color: #c5c5c5;
- --kbd-background: #314559;
- --kbd-box-shadow-color: #5c6773;
- --rust-logo-filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
- brightness(94%) contrast(94%);
- --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
- brightness(113%) contrast(76%);
- --crate-search-hover-border: #e0e0e0;
- --src-sidebar-background-selected: #14191f;
- --src-sidebar-background-hover: #14191f;
- --table-alt-row-background-color: #191f26;
- --codeblock-link-background: #333;
- --scrape-example-toggle-line-background: #999;
- --scrape-example-toggle-line-hover-background: #c5c5c5;
- --scrape-example-code-line-highlight: rgb(91, 59, 1);
- --scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
- --scrape-example-help-border-color: #aaa;
- --scrape-example-help-color: #eee;
- --scrape-example-help-hover-border-color: #fff;
- --scrape-example-help-hover-color: #fff;
- --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
- --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
-}
-
-h1, h2, h3, h4,
-h1 a, .sidebar h2 a, .sidebar h3 a,
-#src-sidebar > .title {
- color: #fff;
-}
-h4 {
- border: none;
-}
-
-.docblock code {
- color: #ffb454;
-}
-.docblock a > code {
- color: #39AFD7 !important;
-}
-.code-header,
-.docblock pre > code,
-pre, pre > code,
-.item-info code,
-.rustdoc.src .example-wrap {
- color: #e6e1cf;
-}
-
-.sidebar .current,
-.sidebar a:hover,
-#src-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#src-sidebar div.files > a:focus, details.dir-entry summary:focus,
-#src-sidebar div.files > a.selected {
- color: #ffb44c;
-}
-
-.sidebar-elems .location {
- color: #ff7733;
-}
-
-.src-line-numbers .line-highlighted {
- color: #708090;
- padding-right: 7px;
- border-right: 1px solid #ffb44c;
-}
-
-.search-results a:hover,
-.search-results a:focus {
- color: #fff !important;
- background-color: #3c3c3c;
-}
-
-.search-results a {
- color: #0096cf;
-}
-.search-results a div.desc {
- color: #c5c5c5;
-}
-
-.result-name .primitive > i, .result-name .keyword > i {
- color: #788797;
-}
-
-#search-tabs > button.selected {
- border-bottom: 1px solid #ffb44c !important;
- border-top: none;
-}
-#search-tabs > button:not(.selected) {
- border: none;
- background-color: transparent !important;
-}
-#search-tabs > button:hover {
- border-bottom: 1px solid rgba(242, 151, 24, 0.3);
-}
-
-#settings-menu > a img {
- filter: invert(100);
-}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
deleted file mode 100644
index 2b3029887..000000000
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ /dev/null
@@ -1,101 +0,0 @@
-:root {
- --main-background-color: #353535;
- --main-color: #ddd;
- --settings-input-color: #2196f3;
- --settings-input-border-color: #999;
- --settings-button-color: #000;
- --settings-button-border-focus: #ffb900;
- --sidebar-background-color: #505050;
- --sidebar-background-color-hover: #676767;
- --code-block-background-color: #2A2A2A;
- --scrollbar-track-background-color: #717171;
- --scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
- --scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
- --headings-border-bottom-color: #d2d2d2;
- --border-color: #e0e0e0;
- --button-background-color: #f0f0f0;
- --right-side-color: grey;
- --code-attribute-color: #999;
- --toggles-color: #999;
- --toggle-filter: invert(100%);
- --search-input-focused-border-color: #008dfd;
- --copy-path-button-color: #999;
- --copy-path-img-filter: invert(50%);
- --copy-path-img-hover-filter: invert(65%);
- --codeblock-error-hover-color: rgb(255, 0, 0);
- --codeblock-error-color: rgba(255, 0, 0, .5);
- --codeblock-ignore-hover-color: rgb(255, 142, 0);
- --codeblock-ignore-color: rgba(255, 142, 0, .6);
- --type-link-color: #2dbfb8;
- --trait-link-color: #b78cf2;
- --assoc-item-link-color: #d2991d;
- --function-link-color: #2bab63;
- --macro-link-color: #09bd00;
- --keyword-link-color: #d2991d;
- --mod-link-color: #d2991d;
- --link-color: #d2991d;
- --sidebar-link-color: #fdbf35;
- --sidebar-current-link-background-color: #444;
- --search-result-link-focus-background-color: #616161;
- --search-result-border-color: #aaa3;
- --search-color: #111;
- --search-error-code-background-color: #484848;
- --search-results-alias-color: #fff;
- --search-results-grey-color: #ccc;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: #252525;
- --search-tab-button-not-selected-background: #252525;
- --search-tab-button-selected-border-top-color: #0089ff;
- --search-tab-button-selected-background: #353535;
- --stab-background-color: #314559;
- --stab-code-color: #e6e1cf;
- --code-highlight-kw-color: #ab8ac1;
- --code-highlight-kw-2-color: #769acb;
- --code-highlight-lifetime-color: #d97f26;
- --code-highlight-prelude-color: #769acb;
- --code-highlight-prelude-val-color: #ee6868;
- --code-highlight-number-color: #83a300;
- --code-highlight-string-color: #83a300;
- --code-highlight-literal-color: #ee6868;
- --code-highlight-attribute-color: #ee6868;
- --code-highlight-self-color: #ee6868;
- --code-highlight-macro-color: #3e999f;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #8d8d8b;
- --code-highlight-doc-comment-color: #8ca375;
- --src-line-numbers-span-color: #3b91e2;
- --src-line-number-highlighted-background-color: #0a042f;
- --test-arrow-color: #dedede;
- --test-arrow-background-color: rgba(78, 139, 202, 0.2);
- --test-arrow-hover-color: #dedede;
- --test-arrow-hover-background-color: rgb(78, 139, 202);
- --target-background-color: #494a3d;
- --target-border-color: #bb7410;
- --kbd-color: #000;
- --kbd-background: #fafbfc;
- --kbd-box-shadow-color: #c6cbd1;
- --rust-logo-filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
- brightness(90%) contrast(90%);
- --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
- brightness(100%) contrast(91%);
- --crate-search-hover-border: #2196f3;
- --src-sidebar-background-selected: #333;
- --src-sidebar-background-hover: #444;
- --table-alt-row-background-color: #2A2A2A;
- --codeblock-link-background: #333;
- --scrape-example-toggle-line-background: #999;
- --scrape-example-toggle-line-hover-background: #c5c5c5;
- --scrape-example-code-line-highlight: rgb(91, 59, 1);
- --scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
- --scrape-example-help-border-color: #aaa;
- --scrape-example-help-color: #eee;
- --scrape-example-help-hover-border-color: #fff;
- --scrape-example-help-hover-color: #fff;
- --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
- --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
-}
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
deleted file mode 100644
index 56fd8cbef..000000000
--- a/src/librustdoc/html/static/css/themes/light.css
+++ /dev/null
@@ -1,98 +0,0 @@
-:root {
- --main-background-color: white;
- --main-color: black;
- --settings-input-color: #2196f3;
- --settings-input-border-color: #717171;
- --settings-button-color: #000;
- --settings-button-border-focus: #717171;
- --sidebar-background-color: #F5F5F5;
- --sidebar-background-color-hover: #E0E0E0;
- --code-block-background-color: #F5F5F5;
- --scrollbar-track-background-color: #dcdcdc;
- --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
- --scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
- --headings-border-bottom-color: #ddd;
- --border-color: #e0e0e0;
- --button-background-color: #fff;
- --right-side-color: grey;
- --code-attribute-color: #999;
- --toggles-color: #999;
- --toggle-filter: none;
- --search-input-focused-border-color: #66afe9;
- --copy-path-button-color: #999;
- --copy-path-img-filter: invert(50%);
- --copy-path-img-hover-filter: invert(35%);
- --codeblock-error-hover-color: rgb(255, 0, 0);
- --codeblock-error-color: rgba(255, 0, 0, .5);
- --codeblock-ignore-hover-color: rgb(255, 142, 0);
- --codeblock-ignore-color: rgba(255, 142, 0, .6);
- --type-link-color: #ad378a;
- --trait-link-color: #6e4fc9;
- --assoc-item-link-color: #3873ad;
- --function-link-color: #ad7c37;
- --macro-link-color: #068000;
- --keyword-link-color: #3873ad;
- --mod-link-color: #3873ad;
- --link-color: #3873ad;
- --sidebar-link-color: #356da4;
- --sidebar-current-link-background-color: #fff;
- --search-result-link-focus-background-color: #ccc;
- --search-result-border-color: #aaa3;
- --search-color: #000;
- --search-error-code-background-color: #d0cccc;
- --search-results-alias-color: #000;
- --search-results-grey-color: #999;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: #e6e6e6;
- --search-tab-button-not-selected-background: #e6e6e6;
- --search-tab-button-selected-border-top-color: #0089ff;
- --search-tab-button-selected-background: #ffffff;
- --stab-background-color: #fff5d6;
- --stab-code-color: #000;
- --code-highlight-kw-color: #8959a8;
- --code-highlight-kw-2-color: #4271ae;
- --code-highlight-lifetime-color: #b76514;
- --code-highlight-prelude-color: #4271ae;
- --code-highlight-prelude-val-color: #c82829;
- --code-highlight-number-color: #718c00;
- --code-highlight-string-color: #718c00;
- --code-highlight-literal-color: #c82829;
- --code-highlight-attribute-color: #c82829;
- --code-highlight-self-color: #c82829;
- --code-highlight-macro-color: #3e999f;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #8e908c;
- --code-highlight-doc-comment-color: #4d4d4c;
- --src-line-numbers-span-color: #c67e2d;
- --src-line-number-highlighted-background-color: #fdffd3;
- --test-arrow-color: #f5f5f5;
- --test-arrow-background-color: rgba(78, 139, 202, 0.2);
- --test-arrow-hover-color: #f5f5f5;
- --test-arrow-hover-background-color: rgb(78, 139, 202);
- --target-background-color: #fdffd3;
- --target-border-color: #ad7c37;
- --kbd-color: #000;
- --kbd-background: #fafbfc;
- --kbd-box-shadow-color: #c6cbd1;
- --rust-logo-filter: initial;
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
- brightness(114%) contrast(76%);
- --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
- brightness(96%) contrast(93%);
- --crate-search-hover-border: #717171;
- --src-sidebar-background-selected: #fff;
- --src-sidebar-background-hover: #e0e0e0;
- --table-alt-row-background-color: #F5F5F5;
- --codeblock-link-background: #eee;
- --scrape-example-toggle-line-background: #ccc;
- --scrape-example-toggle-line-hover-background: #999;
- --scrape-example-code-line-highlight: #fcffd6;
- --scrape-example-code-line-highlight-focus: #f6fdb0;
- --scrape-example-help-border-color: #555;
- --scrape-example-help-color: #333;
- --scrape-example-help-hover-border-color: #000;
- --scrape-example-help-hover-color: #000;
- --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
- --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
-}
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index f697abd07..c7811b43d 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,7 +9,7 @@ function initSearch(searchIndex){}
/**
* @typedef {{
* name: string,
- * id: integer,
+ * id: integer|null,
* fullPath: Array<string>,
* pathWithoutLast: Array<string>,
* pathLast: string,
@@ -37,6 +37,7 @@ let ParserState;
* args: Array<QueryElement>,
* returned: Array<QueryElement>,
* foundElems: number,
+ * totalElems: number,
* literalSearch: boolean,
* corrections: Array<{from: string, to: integer}>,
* }}
@@ -103,7 +104,7 @@ let ResultObject;
*
* fn something() -> Result<usize, usize>
*
- * If output was allowed to be any RawFunctionType, it would look like this
+ * If output was allowed to be any RawFunctionType, it would look like thi
*
* [[], [50, [3, 3]]]
*
@@ -113,10 +114,56 @@ let ResultObject;
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
*
+ * The output can be skipped if it's actually unit and there's no type constraints. If thi
+ * function accepts constrained generics, then the output will be unconditionally emitted, and
+ * after it will come a list of trait constraints. The position of the item in the list will
+ * determine which type parameter it is. For example:
+ *
+ * [1, 2, 3, 4, 5]
+ * ^ ^ ^ ^ ^
+ * | | | | - generic parameter (-3) of trait 5
+ * | | | - generic parameter (-2) of trait 4
+ * | | - generic parameter (-1) of trait 3
+ * | - this function returns a single value (type 2)
+ * - this function takes a single input parameter (type 1)
+ *
+ * Or, for a less contrived version:
+ *
+ * [[[4, -1], 3], [[5, -1]], 11]
+ * -^^^^^^^---- ^^^^^^^ ^^
+ * | | | - generic parameter, roughly `where -1: 11`
+ * | | | since -1 is the type parameter and 11 the trait
+ * | | - function output 5<-1>
+ * | - the overall function signature is something like
+ * | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
+ * - function input, corresponds roughly to 4<-1>
+ * 4 is an index into the `p` array for a type
+ * -1 is the generic parameter, given by 11
+ *
+ * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
+ * function inputs and outputs:
+ *
+ * [-1, -1, [4, 3]]
+ * ^^^^^^ where -1: 4 + 3
+ *
+ * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
+ * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
+ * favor of `4 + 3`:
+ *
+ * [-1, -1, [[4, 3]]]
+ * ^^^^^^^^ where -1: 4 + 3
+ *
+ * [-1, -1, [5, [4, 3]]]
+ * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
+ *
+ * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
+ * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
+ *
* @typedef {(
* 0 |
* [(number|Array<RawFunctionType>)] |
- * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
+ * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
+ * Array<(number|Array<RawFunctionType>)>
* )}
*/
let RawFunctionSearchType;
@@ -136,6 +183,7 @@ let RawFunctionType;
* @typedef {{
* inputs: Array<FunctionType>,
* output: Array<FunctionType>,
+ * where_clause: Array<Array<FunctionType>>,
* }}
*/
let FunctionSearchType;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 254b0d8bf..eb256455b 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -49,10 +49,12 @@ window.currentCrate = getVar("current-crate");
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
// but with the current code it's hard to get the right information in the right place.
- const mobileLocationTitle = document.querySelector(".mobile-topbar h2");
+ const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileLocationTitle && locationTitle) {
- mobileLocationTitle.innerHTML = locationTitle.innerHTML;
+ if (mobileTopbar && locationTitle) {
+ const mobileTitle = document.createElement("h2");
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTopbar.appendChild(mobileTitle);
}
}
@@ -176,13 +178,6 @@ function browserSupportsHistoryApi() {
return window.history && typeof window.history.pushState === "function";
}
-function loadCss(cssUrl) {
- const link = document.createElement("link");
- link.href = cssUrl;
- link.rel = "stylesheet";
- document.getElementsByTagName("head")[0].appendChild(link);
-}
-
function preLoadCss(cssUrl) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
const link = document.createElement("link");
@@ -210,11 +205,7 @@ function preLoadCss(cssUrl) {
event.preventDefault();
// Sending request for the CSS and the JS files at the same time so it will
// hopefully be loaded when the JS will generate the settings content.
- loadCss(getVar("static-root-path") + getVar("settings-css"));
loadScript(getVar("static-root-path") + getVar("settings-js"));
- preLoadCss(getVar("static-root-path") + getVar("theme-light-css"));
- preLoadCss(getVar("static-root-path") + getVar("theme-dark-css"));
- preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css"));
// Pre-load all theme CSS files, so that switching feels seamless.
//
// When loading settings.html as a standalone page, the equivalent HTML is
@@ -499,7 +490,7 @@ function preLoadCss(cssUrl) {
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
- block("type", "types", "Type Definitions");
+ block("type", "types", "Type Aliases");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
block("traitalias", "trait-aliases", "Trait Aliases");
@@ -852,14 +843,14 @@ function preLoadCss(cssUrl) {
window.CURRENT_TOOLTIP_ELEMENT = wrapper;
window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e;
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
- wrapper.onpointerenter = function(ev) {
+ wrapper.onpointerenter = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
clearTooltipHoverTimeout(e);
};
- wrapper.onpointerleave = function(ev) {
+ wrapper.onpointerleave = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
@@ -963,38 +954,38 @@ function preLoadCss(cssUrl) {
}
onEachLazy(document.getElementsByClassName("tooltip"), e => {
- e.onclick = function() {
- this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true;
- if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) {
+ e.onclick = () => {
+ e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true;
+ if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) {
hideTooltip(true);
} else {
- showTooltip(this);
+ showTooltip(e);
window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0");
window.CURRENT_TOOLTIP_ELEMENT.focus();
window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler;
}
return false;
};
- e.onpointerenter = function(ev) {
+ e.onpointerenter = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- setTooltipHoverTimeout(this, true);
+ setTooltipHoverTimeout(e, true);
};
- e.onpointermove = function(ev) {
+ e.onpointermove = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- setTooltipHoverTimeout(this, true);
+ setTooltipHoverTimeout(e, true);
};
- e.onpointerleave = function(ev) {
+ e.onpointerleave = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- if (!this.TOOLTIP_FORCE_VISIBLE &&
+ if (!e.TOOLTIP_FORCE_VISIBLE &&
!elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) {
// Tooltip pointer leave gesture:
//
@@ -1139,7 +1130,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
*
* Pass "true" to reset focus for tooltip popovers.
*/
- window.hideAllModals = function(switchFocus) {
+ window.hideAllModals = switchFocus => {
hideSidebar();
window.hidePopoverMenus();
hideTooltip(switchFocus);
@@ -1148,7 +1139,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
/**
* Hide all the popover menus.
*/
- window.hidePopoverMenus = function() {
+ window.hidePopoverMenus = () => {
onEachLazy(document.querySelectorAll(".search-form .popover"), elem => {
elem.style.display = "none";
});
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 42088e735..2f0cae0a4 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -3,6 +3,17 @@
"use strict";
+// polyfill
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
+if (!Array.prototype.toSpliced) {
+ // Can't use arrow functions, because we want `this`
+ Array.prototype.toSpliced = function() {
+ const me = this.slice();
+ Array.prototype.splice.apply(me, arguments);
+ return me;
+ };
+}
+
(function() {
// This mapping table should match the discriminants of
// `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -33,6 +44,7 @@ const itemTypes = [
"attr",
"derive",
"traitalias",
+ "generic",
];
const longItemTypes = [
@@ -67,6 +79,7 @@ const longItemTypes = [
// used for special search precedence
const TY_PRIMITIVE = itemTypes.indexOf("primitive");
const TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_GENERIC = itemTypes.indexOf("generic");
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
function hasOwnPropertyRustdoc(obj, property) {
@@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) {
/**
* Add an item to the type Name->ID map, or, if one already exists, use it.
- * Returns the number. If name is "" or null, return -1 (pure generic).
+ * Returns the number. If name is "" or null, return null (pure generic).
*
* This is effectively string interning, so that function matching can be
* done more quickly. Two types with the same name but different item kinds
@@ -263,9 +276,8 @@ function initSearch(rawSearchIndex) {
* @returns {integer}
*/
function buildTypeMapIndex(name) {
-
if (name === "" || name === null) {
- return -1;
+ return null;
}
if (typeNameIdMap.has(name)) {
@@ -490,7 +502,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: "never",
- id: -1,
+ id: null,
fullPath: ["never"],
pathWithoutLast: [],
pathLast: "never",
@@ -532,7 +544,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: name.trim(),
- id: -1,
+ id: null,
fullPath: pathSegments,
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
pathLast: pathSegments[pathSegments.length - 1],
@@ -661,7 +673,7 @@ function initSearch(rawSearchIndex) {
}
elems.push({
name: "[]",
- id: -1,
+ id: null,
fullPath: ["[]"],
pathWithoutLast: [],
pathLast: "[]",
@@ -972,9 +984,13 @@ function initSearch(rawSearchIndex) {
returned: [],
// Total number of "top" elements (does not include generics).
foundElems: 0,
+ // Total number of elements (includes generics).
+ totalElems: 0,
literalSearch: false,
error: null,
correction: null,
+ proposeCorrectionFrom: null,
+ proposeCorrectionTo: null,
};
}
@@ -1015,64 +1031,10 @@ function initSearch(rawSearchIndex) {
/**
* Parses the query.
*
- * The supported syntax by this parser is as follow:
- *
- * ident = *(ALPHA / DIGIT / "_")
- * path = ident *(DOUBLE-COLON/{WS} ident) [!]
- * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
- * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
- * type-sep = *WS COMMA *(COMMA)
- * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
- * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
- * CLOSE-ANGLE-BRACKET
- * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
- *
- * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
- * type-search = [ nonempty-arg-list ] [ return-args ]
- *
- * query = *WS (exact-search / type-search) *WS
+ * The supported syntax by this parser is given in the rustdoc book chapter
+ * /src/doc/rustdoc/src/read-documentation/search.md
*
- * type-filter = (
- * "mod" /
- * "externcrate" /
- * "import" /
- * "struct" /
- * "enum" /
- * "fn" /
- * "type" /
- * "static" /
- * "trait" /
- * "impl" /
- * "tymethod" /
- * "method" /
- * "structfield" /
- * "variant" /
- * "macro" /
- * "primitive" /
- * "associatedtype" /
- * "constant" /
- * "associatedconstant" /
- * "union" /
- * "foreigntype" /
- * "keyword" /
- * "existential" /
- * "attr" /
- * "derive" /
- * "traitalias")
- *
- * OPEN-ANGLE-BRACKET = "<"
- * CLOSE-ANGLE-BRACKET = ">"
- * OPEN-SQUARE-BRACKET = "["
- * CLOSE-SQUARE-BRACKET = "]"
- * COLON = ":"
- * DOUBLE-COLON = "::"
- * QUOTE = %x22
- * COMMA = ","
- * RETURN-ARROW = "->"
- *
- * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- * DIGIT = %x30-39
- * WS = %x09 / " "
+ * When adding new things to the parser, add them there, too!
*
* @param {string} val - The user query
*
@@ -1125,6 +1087,7 @@ function initSearch(rawSearchIndex) {
query.literalSearch = parserState.totalElems > 1;
}
query.foundElems = query.elems.length + query.returned.length;
+ query.totalElems = parserState.totalElems;
return query;
}
@@ -1173,7 +1136,7 @@ function initSearch(rawSearchIndex) {
const out = [];
for (const result of results) {
- if (result.id > -1) {
+ if (result.id !== -1) {
const obj = searchIndex[result.id];
obj.dist = result.dist;
const res = buildHrefAndPath(obj);
@@ -1349,166 +1312,311 @@ function initSearch(rawSearchIndex) {
* This function checks generics in search query `queryElem` can all be found in the
* search index (`fnType`),
*
- * @param {FunctionType} fnType - The object to check.
- * @param {QueryElement} queryElem - The element from the parsed query.
+ * This function returns `true` if it matches, and also writes the results to mgensInout.
+ * It returns `false` if no match is found, and leaves mgensInout untouched.
+ *
+ * @param {FunctionType} fnType - The object to check.
+ * @param {QueryElement} queryElem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function checkGenerics(fnType, queryElem) {
- return unifyFunctionTypes(fnType.generics, queryElem.generics);
+ function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
+ return unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgensInout,
+ mgens => {
+ if (mgensInout) {
+ for (const [fid, qid] of mgens.entries()) {
+ mgensInout.set(fid, qid);
+ }
+ }
+ return true;
+ }
+ );
}
/**
* This function checks if a list of search query `queryElems` can all be found in the
* search index (`fnTypes`).
*
- * @param {Array<FunctionType>} fnTypes - The objects to check.
+ * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+ * supplied, it will call that function with mgens, and that callback can accept or
+ * reject the result bu returning `true` or `false`. If the callback returns false,
+ * then this function will try with a different solution, or bail with false if it
+ * runs out of candidates.
+ *
+ * @param {Array<FunctionType>} fnTypes - The objects to check.
* @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensIn
+ * - Map functions generics to query generics (never modified).
+ * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function unifyFunctionTypes(fnTypes, queryElems) {
- // This search engine implements order-agnostic unification. There
- // should be no missing duplicates (generics have "bag semantics"),
- // and the row is allowed to have extras.
+ function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
+ /**
+ * @type Map<integer, integer>
+ */
+ let mgens = new Map(mgensIn);
if (queryElems.length === 0) {
- return true;
+ return !solutionCb || solutionCb(mgens);
}
- if (!fnTypes || fnTypes.length === 0) {
+ if (!fnTypesIn || fnTypesIn.length === 0) {
return false;
}
+ const ql = queryElems.length;
+ let fl = fnTypesIn.length;
/**
- * @type Map<integer, QueryElement[]>
+ * @type Array<FunctionType>
*/
- const queryElemSet = new Map();
- const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet(queryElem) {
- let currentQueryElemList;
- if (queryElemSet.has(queryElem.id)) {
- currentQueryElemList = queryElemSet.get(queryElem.id);
- } else {
- currentQueryElemList = [];
- queryElemSet.set(queryElem.id, currentQueryElemList);
- }
- currentQueryElemList.push(queryElem);
- };
- for (const queryElem of queryElems) {
- addQueryElemToQueryElemSet(queryElem);
- }
+ let fnTypes = fnTypesIn.slice();
/**
- * @type Map<integer, FunctionType[]>
+ * loop works by building up a solution set in the working arrays
+ * fnTypes gets mutated in place to make this work, while queryElems
+ * is left alone
+ *
+ * vvvvvvv `i` points here
+ * queryElems = [ good, good, good, unknown, unknown ],
+ * fnTypes = [ good, good, good, unknown, unknown ],
+ * ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
+ * | looking for candidates
+ * everything before `i` is the
+ * current working solution
+ *
+ * Everything in the current working solution is known to be a good
+ * match, but it might not be the match we wind up going with, because
+ * there might be more than one candidate match, and we need to try them all
+ * before giving up. So, to handle this, it backtracks on failure.
+ *
+ * @type Array<{
+ * "fnTypesScratch": Array<FunctionType>,
+ * "queryElemsOffset": integer,
+ * "fnTypesOffset": integer
+ * }>
*/
- const fnTypeSet = new Map();
- const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet(fnType) {
- // Pure generic, or an item that's not matched by any query elems.
- // Try [unboxing] it.
- //
- // [unboxing]:
- // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
- if (fnType.id === -1 || !(
- queryElemSet.has(fnType.id) ||
- (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
- (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
- )) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
- }
- return;
- }
- let currentQueryElemList = queryElemSet.get(fnType.id) || [];
- let matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- if (matchIdx === -1 &&
- (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
- queryContainsArrayOrSliceElem
- ) {
- currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
- matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- }
- // None of the query elems match the function type.
- // Try [unboxing] it.
- if (matchIdx === -1) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
+ const backtracking = [];
+ let i = 0;
+ let j = 0;
+ const backtrack = () => {
+ while (backtracking.length !== 0) {
+ // this session failed, but there are other possible solutions
+ // to backtrack, reset to (a copy of) the old array, do the swap or unboxing
+ const {
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset,
+ fnTypesOffset,
+ unbox,
+ } = backtracking.pop();
+ mgens = new Map(mgensScratch);
+ const fnType = fnTypesScratch[fnTypesOffset];
+ const queryElem = queryElems[queryElemsOffset];
+ if (unbox) {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ continue;
+ }
+ mgens.set(fnType.id, 0);
+ }
+ const generics = fnType.id < 0 ?
+ whereClause[(-fnType.id) - 1] :
+ fnType.generics;
+ fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
+ fl = fnTypes.length;
+ // re-run the matching algorithm on this item
+ i = queryElemsOffset - 1;
+ } else {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ continue;
+ }
+ mgens.set(fnType.id, queryElem.id);
+ }
+ fnTypes = fnTypesScratch.slice();
+ fl = fnTypes.length;
+ const tmp = fnTypes[queryElemsOffset];
+ fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+ fnTypes[fnTypesOffset] = tmp;
+ // this is known as a good match; go to the next one
+ i = queryElemsOffset;
}
- return;
- }
- let currentFnTypeList;
- if (fnTypeSet.has(fnType.id)) {
- currentFnTypeList = fnTypeSet.get(fnType.id);
- } else {
- currentFnTypeList = [];
- fnTypeSet.set(fnType.id, currentFnTypeList);
+ return true;
}
- currentFnTypeList.push(fnType);
+ return false;
};
- for (const fnType of fnTypes) {
- addFnTypeToFnTypeSet(fnType);
- }
- const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
- if (queryElemList.length === 0) {
- return true;
+ for (i = 0; i !== ql; ++i) {
+ const queryElem = queryElems[i];
+ /**
+ * list of potential function types that go with the current query element.
+ * @type Array<integer>
+ */
+ const matchCandidates = [];
+ let fnTypesScratch = null;
+ let mgensScratch = null;
+ // don't try anything before `i`, because they've already been
+ // paired off with the other query elements
+ for (j = i; j !== fl; ++j) {
+ const fnType = fnTypes[j];
+ if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgens,
+ mgensScratch => {
+ matchCandidates.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: false,
+ });
+ return false; // "reject" all candidates to gather all of them
+ }
+ );
+ }
+ if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ if (!mgensScratch) {
+ mgensScratch = new Map(mgens);
+ }
+ backtracking.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: true,
+ });
+ }
}
- // Multiple items in one list might match multiple items in another.
- // Since an item with fewer generics can match an item with more, we
- // need to check all combinations for a potential match.
- const queryElem = queryElemList.pop();
- const l = currentFnTypeList.length;
- for (let i = 0; i < l; i += 1) {
- const fnType = currentFnTypeList[i];
- if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
+ if (matchCandidates.length === 0) {
+ if (backtrack()) {
continue;
+ } else {
+ return false;
}
- if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
- currentFnTypeList.splice(i, 1);
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- return true;
- }
- currentFnTypeList.splice(i, 0, fnType);
+ }
+ // use the current candidate
+ const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+ if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
+ mgens.set(fnTypes[candidate].id, queryElems[i].id);
+ }
+ for (const [fid, qid] of mgensNew) {
+ mgens.set(fid, qid);
+ }
+ // `i` and `j` are paired off
+ // `queryElems[i]` is left in place
+ // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
+ const tmp = fnTypes[candidate];
+ fnTypes[candidate] = fnTypes[i];
+ fnTypes[i] = tmp;
+ // write other candidates to backtracking queue
+ for (const otherCandidate of matchCandidates) {
+ backtracking.push(otherCandidate);
+ }
+ // If we're on the last item, check the solution with the callback
+ // backtrack if the callback says its unsuitable
+ while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+ if (!backtrack()) {
+ return false;
}
}
+ }
+ return true;
+ }
+ function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
+ // type filters look like `trait:Read` or `enum:Result`
+ if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
return false;
- };
- const handleQueryElemList = (id, queryElemList) => {
- if (!fnTypeSet.has(id)) {
- if (id === typeNameIdOfArrayOrSlice) {
- return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
- handleQueryElemList(typeNameIdOfArray, queryElemList);
+ }
+ // fnType.id < 0 means generic
+ // queryElem.id < 0 does too
+ // mgens[fnType.id] = queryElem.id
+ // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
+ // and should make that same decision everywhere it appears
+ if (fnType.id < 0 && queryElem.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ return false;
+ }
+ for (const [fid, qid] of mgens.entries()) {
+ if (fnType.id !== fid && queryElem.id === qid) {
+ return false;
+ }
+ if (fnType.id === fid && queryElem.id !== qid) {
+ return false;
}
+ }
+ } else if (fnType.id !== null) {
+ if (queryElem.id === typeNameIdOfArrayOrSlice &&
+ (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+ ) {
+ // [] matches primitive:array or primitive:slice
+ // if it matches, then we're fine, and this is an appropriate match candidate
+ } else if (fnType.id !== queryElem.id) {
return false;
}
- const currentFnTypeList = fnTypeSet.get(id);
- if (currentFnTypeList.length < queryElemList.length) {
- // It's not possible for all the query elems to find a match.
+ // If the query elem has generics, and the function doesn't,
+ // it can't match.
+ if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
return false;
}
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- // Found a solution.
- // Any items that weren't used for it can be unboxed, and might form
- // part of the solution for another item.
- for (const innerFnType of currentFnTypeList) {
- addFnTypeToFnTypeSet(innerFnType);
+ // If the query element is a path (it contains `::`), we need to check if this
+ // path is compatible with the target type.
+ const queryElemPathLength = queryElem.pathWithoutLast.length;
+ if (queryElemPathLength > 0) {
+ const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+ fnType.path.split("::") : [];
+ // If the path provided in the query element is longer than this type,
+ // no need to check it since it won't match in any case.
+ if (queryElemPathLength > fnTypePath.length) {
+ return false;
}
- fnTypeSet.delete(id);
- }
- return result;
- };
- let queryElemSetSize = -1;
- while (queryElemSetSize !== queryElemSet.size) {
- queryElemSetSize = queryElemSet.size;
- for (const [id, queryElemList] of queryElemSet) {
- if (handleQueryElemList(id, queryElemList)) {
- queryElemSet.delete(id);
+ let i = 0;
+ for (const path of fnTypePath) {
+ if (path === queryElem.pathWithoutLast[i]) {
+ i += 1;
+ if (i >= queryElemPathLength) {
+ break;
+ }
+ }
+ }
+ if (i < queryElemPathLength) {
+ // If we didn't find all parts of the path of the query element inside
+ // the fn type, then it's not the right one.
+ return false;
}
}
}
- return queryElemSetSize === 0;
+ return true;
+ }
+ function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
+ if (fnType.id < 0 && queryElem.id >= 0) {
+ if (!whereClause) {
+ return false;
+ }
+ // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
+ // mgens[fnType.id] === null indicates that we haven't decided yet
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ return false;
+ }
+ // This is only a potential unbox if the search query appears in the where clause
+ // for example, searching `Read -> usize` should find
+ // `fn read_all<R: Read>(R) -> Result<usize>`
+ // generic `R` is considered "unboxed"
+ return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+ } else if (fnType.generics && fnType.generics.length > 0) {
+ return checkIfInList(fnType.generics, queryElem, whereClause);
+ }
+ return false;
}
/**
@@ -1516,13 +1624,14 @@ function initSearch(rawSearchIndex) {
* generics (if any).
*
* @param {Array<FunctionType>} list
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if found, false otherwise.
*/
- function checkIfInList(list, elem) {
+ function checkIfInList(list, elem, whereClause) {
for (const entry of list) {
- if (checkType(entry, elem)) {
+ if (checkType(entry, elem, whereClause)) {
return true;
}
}
@@ -1534,14 +1643,26 @@ function initSearch(rawSearchIndex) {
* generics (if any).
*
* @param {Row} row
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if the type matches, false otherwise.
*/
- function checkType(row, elem) {
- if (row.id === -1) {
+ function checkType(row, elem, whereClause) {
+ if (row.id === null) {
// This is a pure "generic" search, no need to run other checks.
- return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
+ return row.generics.length > 0
+ ? checkIfInList(row.generics, elem, whereClause)
+ : false;
+ }
+
+ if (row.id < 0 && elem.id >= 0) {
+ const gid = (-row.id) - 1;
+ return checkIfInList(whereClause[gid], elem, whereClause);
+ }
+
+ if (row.id < 0 && elem.id < 0) {
+ return true;
}
const matchesExact = row.id === elem.id;
@@ -1551,7 +1672,7 @@ function initSearch(rawSearchIndex) {
if ((matchesExact || matchesArrayOrSlice) &&
typePassesFilter(elem.typeFilter, row.ty)) {
if (elem.generics.length > 0) {
- return checkGenerics(row, elem);
+ return checkGenerics(row, elem, whereClause, new Map());
}
return true;
}
@@ -1559,7 +1680,7 @@ function initSearch(rawSearchIndex) {
// If the current item does not match, try [unboxing] the generic.
// [unboxing]:
// https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- return checkIfInList(row.generics, elem);
+ return checkIfInList(row.generics, elem, whereClause);
}
function checkPath(contains, ty, maxEditDistance) {
@@ -1760,13 +1881,15 @@ function initSearch(rawSearchIndex) {
const fullId = row.id;
const searchWord = searchWords[pos];
- const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
+ const in_args = row.type && row.type.inputs
+ && checkIfInList(row.type.inputs, elem, row.type.where_clause);
if (in_args) {
// path_dist is 0 because no parent path information is currently stored
// in the search index
addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
}
- const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
+ const returned = row.type && row.type.output
+ && checkIfInList(row.type.output, elem, row.type.where_clause);
if (returned) {
addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
}
@@ -1828,10 +1951,20 @@ function initSearch(rawSearchIndex) {
}
// If the result is too "bad", we return false and it ends this search.
- if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
- return;
- }
- if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
+ if (!unifyFunctionTypes(
+ row.type.inputs,
+ parsedQuery.elems,
+ row.type.where_clause,
+ null,
+ mgens => {
+ return unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause,
+ mgens
+ );
+ }
+ )) {
return;
}
@@ -1851,6 +1984,11 @@ function initSearch(rawSearchIndex) {
const maxEditDistance = Math.floor(queryLen / 3);
/**
+ * @type {Map<string, integer>}
+ */
+ const genericSymbols = new Map();
+
+ /**
* Convert names to ids in parsed query elements.
* This is not used for the "In Names" tab, but is used for the
* "In Params", "In Returns", and "In Function Signature" tabs.
@@ -1863,14 +2001,14 @@ function initSearch(rawSearchIndex) {
* @param {QueryElement} elem
*/
function convertNameToId(elem) {
- if (typeNameIdMap.has(elem.name)) {
- elem.id = typeNameIdMap.get(elem.name);
+ if (typeNameIdMap.has(elem.pathLast)) {
+ elem.id = typeNameIdMap.get(elem.pathLast);
} else if (!parsedQuery.literalSearch) {
- let match = -1;
+ let match = null;
let matchDist = maxEditDistance + 1;
let matchName = "";
for (const [name, id] of typeNameIdMap) {
- const dist = editDistance(name, elem.name, maxEditDistance);
+ const dist = editDistance(name, elem.pathLast, maxEditDistance);
if (dist <= matchDist && dist <= maxEditDistance) {
if (dist === matchDist && matchName > name) {
continue;
@@ -1880,11 +2018,52 @@ function initSearch(rawSearchIndex) {
matchName = name;
}
}
- if (match !== -1) {
+ if (match !== null) {
parsedQuery.correction = matchName;
}
elem.id = match;
}
+ if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
+ && elem.generics.length === 0)
+ || elem.typeFilter === TY_GENERIC) {
+ if (genericSymbols.has(elem.name)) {
+ elem.id = genericSymbols.get(elem.name);
+ } else {
+ elem.id = -(genericSymbols.size + 1);
+ genericSymbols.set(elem.name, elem.id);
+ }
+ if (elem.typeFilter === -1 && elem.name.length >= 3) {
+ // Silly heuristic to catch if the user probably meant
+ // to not write a generic parameter. We don't use it,
+ // just bring it up.
+ const maxPartDistance = Math.floor(elem.name.length / 3);
+ let matchDist = maxPartDistance + 1;
+ let matchName = "";
+ for (const name of typeNameIdMap.keys()) {
+ const dist = editDistance(name, elem.name, maxPartDistance);
+ if (dist <= matchDist && dist <= maxPartDistance) {
+ if (dist === matchDist && matchName > name) {
+ continue;
+ }
+ matchDist = dist;
+ matchName = name;
+ }
+ }
+ if (matchName !== "") {
+ parsedQuery.proposeCorrectionFrom = elem.name;
+ parsedQuery.proposeCorrectionTo = matchName;
+ }
+ }
+ elem.typeFilter = TY_GENERIC;
+ }
+ if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
+ // Rust does not have HKT
+ parsedQuery.error = [
+ "Generic type parameter ",
+ elem.name,
+ " does not accept generic parameters",
+ ];
+ }
for (const elem2 of elem.generics) {
convertNameToId(elem2);
}
@@ -1918,8 +2097,11 @@ function initSearch(rawSearchIndex) {
elem = parsedQuery.returned[0];
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
- in_returned = row.type &&
- unifyFunctionTypes(row.type.output, parsedQuery.returned);
+ in_returned = row.type && unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause
+ );
if (in_returned) {
addIntoResults(
results_others,
@@ -2152,11 +2334,20 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
function makeTabHeader(tabNb, text, nbElems) {
+ // https://blog.horizon-eda.org/misc/2020/02/19/ui.html
+ //
+ // CSS runs with `font-variant-numeric: tabular-nums` to ensure all
+ // digits are the same width. \u{2007} is a Unicode space character
+ // that is defined to be the same width as a digit.
+ const fmtNbElems =
+ nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` :
+ nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` :
+ `\u{2007}(${nbElems})`;
if (searchState.currentTab === tabNb) {
return "<button class=\"selected\">" + text +
- " <span class=\"count\">(" + nbElems + ")</span></button>";
+ "<span class=\"count\">" + fmtNbElems + "</span></button>";
}
- return "<button>" + text + " <span class=\"count\">(" + nbElems + ")</span></button>";
+ return "<button>" + text + "<span class=\"count\">" + fmtNbElems + "</span></button>";
}
/**
@@ -2270,6 +2461,13 @@ ${item.displayPath}<span class="${type}">${name}</span>\
"Showing results for closest type name " +
`"${results.query.correction}" instead.</h3>`;
}
+ if (results.query.proposeCorrectionFrom !== null) {
+ const orig = results.query.proposeCorrectionFrom;
+ const targ = results.query.proposeCorrectionTo;
+ output += "<h3 class=\"search-corrections\">" +
+ `Type "${orig}" not found and used as generic parameter. ` +
+ `Consider searching for "${targ}" instead.</h3>`;
+ }
const resultsElem = document.createElement("div");
resultsElem.id = "results";
@@ -2371,29 +2569,54 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* @return {Array<FunctionSearchType>}
*/
function buildItemSearchTypeAll(types, lowercasePaths) {
+ return types.map(type => buildItemSearchType(type, lowercasePaths));
+ }
+
+ /**
+ * Converts a single type.
+ *
+ * @param {RawFunctionType} type
+ */
+ function buildItemSearchType(type, lowercasePaths) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
- return types.map(type => {
- let pathIndex, generics;
- if (typeof type === "number") {
- pathIndex = type;
- generics = [];
- } else {
- pathIndex = type[PATH_INDEX_DATA];
- generics = buildItemSearchTypeAll(
- type[GENERICS_DATA],
- lowercasePaths
- );
- }
+ let pathIndex, generics;
+ if (typeof type === "number") {
+ pathIndex = type;
+ generics = [];
+ } else {
+ pathIndex = type[PATH_INDEX_DATA];
+ generics = buildItemSearchTypeAll(
+ type[GENERICS_DATA],
+ lowercasePaths
+ );
+ }
+ if (pathIndex < 0) {
+ // types less than 0 are generic parameters
+ // the actual names of generic parameters aren't stored, since they aren't API
return {
- // `0` is used as a sentinel because it's fewer bytes than `null`
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: generics,
+ id: pathIndex,
+ ty: TY_GENERIC,
+ path: null,
+ generics,
};
- });
+ }
+ if (pathIndex === 0) {
+ // `0` is used as a sentinel because it's fewer bytes than `null`
+ return {
+ id: null,
+ ty: null,
+ path: null,
+ generics,
+ };
+ }
+ const item = lowercasePaths[pathIndex - 1];
+ return {
+ id: buildTypeMapIndex(item.name),
+ ty: item.ty,
+ path: item.path,
+ generics,
+ };
}
/**
@@ -2421,14 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") {
- const pathIndex = functionSearchType[INPUTS_DATA];
- inputs = [{
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: [],
- }];
+ inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
@@ -2437,14 +2653,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
- const pathIndex = functionSearchType[OUTPUT_DATA];
- output = [{
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: [],
- }];
+ output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
@@ -2454,8 +2663,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
} else {
output = [];
}
+ const where_clause = [];
+ const l = functionSearchType.length;
+ for (let i = 2; i < l; ++i) {
+ where_clause.push(typeof functionSearchType[i] === "number"
+ ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
+ : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+ }
return {
- inputs, output,
+ inputs, output, where_clause,
};
}
@@ -2486,18 +2702,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
let crateSize = 0;
/**
- * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
- * are arrays with the same length. n[i] contains the name of an item.
- * t[i] contains the type of that item (as a string of characters that represent an
- * offset in `itemTypes`). d[i] contains the description of that item.
+ * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f`
+ * are arrays with the same length. `q`, `a`, and `c` use a sparse
+ * representation for compactness.
+ *
+ * `n[i]` contains the name of an item.
+ *
+ * `t[i]` contains the type of that item
+ * (as a string of characters that represent an offset in `itemTypes`).
*
- * q[i] contains the full path of the item, or an empty string indicating
- * "same as q[i-1]".
+ * `d[i]` contains the description of that item.
*
- * i[i] contains an item's parent, usually a module. For compactness,
+ * `q` contains the full paths of the items. For compactness, it is a set of
+ * (index, path) pairs used to create a map. If a given index `i` is
+ * not present, this indicates "same as the last index present".
+ *
+ * `i[i]` contains an item's parent, usually a module. For compactness,
* it is a set of indexes into the `p` array.
*
- * f[i] contains function signatures, or `0` if the item isn't a function.
+ * `f[i]` contains function signatures, or `0` if the item isn't a function.
* Functions are themselves encoded as arrays. The first item is a list of
* types representing the function's inputs, and the second list item is a list
* of types representing the function's output. Tuples are flattened.
@@ -2511,6 +2734,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
*
* `p` is a list of path/type pairs. It is used for parents and function parameters.
*
+ * `c` is an array of item indices that are deprecated.
+ *
* @type {{
* doc: string,
* a: Object,
@@ -2577,9 +2802,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// convert `rawPaths` entries into object form
// generate normalizedPaths for function search mode
let len = paths.length;
+ let lastPath = itemPaths.get(0);
for (let i = 0; i < len; ++i) {
- lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()});
- paths[i] = {ty: paths[i][0], name: paths[i][1]};
+ const elem = paths[i];
+ const ty = elem[0];
+ const name = elem[1];
+ let path = null;
+ if (elem.length > 2) {
+ path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
+ lastPath = path;
+ }
+
+ lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
+ paths[i] = {ty: ty, name: name, path: path};
}
// convert `item*` into an object form, and construct word indices.
@@ -2589,8 +2824,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
+ lastPath = "";
len = itemTypes.length;
- let lastPath = "";
for (let i = 0; i < len; ++i) {
let word = "";
// This object should have exactly the same set of fields as the "crateRow"
@@ -2599,11 +2834,12 @@ ${item.displayPath}<span class="${type}">${name}</span>\
word = itemNames[i].toLowerCase();
}
searchWords.push(word);
+ const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
const row = {
crate: crate,
ty: itemTypes.charCodeAt(i) - charA,
name: itemNames[i],
- path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
+ path: path,
desc: itemDescs[i],
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType(
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 2cba32c1b..63947789c 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -59,8 +59,8 @@
if (settingValue !== null) {
toggle.checked = settingValue === "true";
}
- toggle.onchange = function() {
- changeSetting(this.id, this.checked);
+ toggle.onchange = () => {
+ changeSetting(toggle.id, toggle.checked);
};
});
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
@@ -224,14 +224,14 @@
if (isSettingsPage) {
// We replace the existing "onclick" callback to do nothing if clicked.
- getSettingsButton().onclick = function(event) {
+ getSettingsButton().onclick = event => {
event.preventDefault();
};
} else {
// We replace the existing "onclick" callback.
const settingsButton = getSettingsButton();
const settingsMenu = document.getElementById("settings");
- settingsButton.onclick = function(event) {
+ settingsButton.onclick = event => {
if (elemIsInParent(event.target, settingsMenu)) {
return;
}
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index af3ca42a6..c69641092 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -5,6 +5,7 @@
// the page, so we don't see major layout changes during the load of the page.
"use strict";
+const builtinThemes = ["light", "dark", "ayu"];
const darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
@@ -119,19 +120,32 @@ function switchTheme(newThemeName, saveTheme) {
updateLocalStorage("theme", newThemeName);
}
- let newHref;
+ document.documentElement.setAttribute("data-theme", newThemeName);
- if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
- newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
+ if (builtinThemes.indexOf(newThemeName) !== -1) {
+ if (window.currentTheme) {
+ window.currentTheme.parentNode.removeChild(window.currentTheme);
+ window.currentTheme = null;
+ }
} else {
- newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
- }
-
- if (!window.currentTheme) {
- document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
- window.currentTheme = document.getElementById("themeStyle");
- } else if (newHref !== window.currentTheme.href) {
- window.currentTheme.href = newHref;
+ const newHref = getVar("root-path") + newThemeName +
+ getVar("resource-suffix") + ".css";
+ if (!window.currentTheme) {
+ // If we're in the middle of loading, document.write blocks
+ // rendering, but if we are done, it would blank the page.
+ if (document.readyState === "loading") {
+ document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
+ window.currentTheme = document.getElementById("themeStyle");
+ } else {
+ window.currentTheme = document.createElement("link");
+ window.currentTheme.rel = "stylesheet";
+ window.currentTheme.id = "themeStyle";
+ window.currentTheme.href = newHref;
+ document.documentElement.appendChild(window.currentTheme);
+ }
+ } else if (newHref !== window.currentTheme.href) {
+ window.currentTheme.href = newHref;
+ }
}
}
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index a27aa2b58..ca9a78f51 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -91,7 +91,6 @@ macro_rules! static_files {
static_files! {
rustdoc_css => "static/css/rustdoc.css",
- settings_css => "static/css/settings.css",
noscript_css => "static/css/noscript.css",
normalize_css => "static/css/normalize.css",
main_js => "static/js/main.js",
@@ -109,9 +108,6 @@ static_files! {
rust_favicon_svg => "static/images/favicon.svg",
rust_favicon_png_16 => "static/images/favicon-16x16.png",
rust_favicon_png_32 => "static/images/favicon-32x32.png",
- theme_light_css => "static/css/themes/light.css",
- theme_dark_css => "static/css/themes/dark.css",
- theme_ayu_css => "static/css/themes/ayu.css",
fira_sans_regular => "static/fonts/FiraSans-Regular.woff2",
fira_sans_medium => "static/fonts/FiraSans-Medium.woff2",
fira_sans_license => "static/fonts/FiraSans-LICENSE.txt",
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 60ccfe4da..579c782be 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -15,8 +15,7 @@
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
<link rel="stylesheet" {#+ #}
- href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
- id="mainThemeStyle"> {# #}
+ href="{{static_root_path|safe}}{{files.rustdoc_css}}"> {# #}
{% if !layout.default_settings.is_empty() %}
<script id="default-settings" {#+ #}
{%~ for (k, v) in layout.default_settings ~%}
@@ -34,10 +33,6 @@
data-channel="{{rust_channel}}" {#+ #}
data-search-js="{{files.search_js}}" {#+ #}
data-settings-js="{{files.settings_js}}" {#+ #}
- data-settings-css="{{files.settings_css}}" {#+ #}
- data-theme-light-css="{{files.theme_light_css}}" {#+ #}
- data-theme-dark-css="{{files.theme_dark_css}}" {#+ #}
- data-theme-ayu-css="{{files.theme_ayu_css}}" {#+ #}
> {# #}
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
{% if page.css_class.contains("crate") %}
@@ -54,12 +49,6 @@
{% endif %}
<noscript> {# #}
<link rel="stylesheet" {#+ #}
- media="(prefers-color-scheme:light)" {#+ #}
- href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
- <link rel="stylesheet" {#+ #}
- media="(prefers-color-scheme:dark)" {#+ #}
- href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
- <link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.noscript_css}}"> {# #}
</noscript> {# #}
{% if layout.css_file_extension.is_some() %}
@@ -95,8 +84,7 @@
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% endif %}
</a> {# #}
- <h2></h2> {# #}
- </nav> {# #}
+ </nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html
index 287cbab07..b8b7785a2 100644
--- a/src/librustdoc/html/templates/type_layout.html
+++ b/src/librustdoc/html/templates/type_layout.html
@@ -9,12 +9,12 @@
<strong>Note:</strong> Most layout information is <strong>completely {#+ #}
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
- attributes. Please see the Rust Reference’s {#+ #}
+ attributes. Please see the Rust Reference's {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}
- <p><strong>Size:</strong> {{ type_layout_size|safe }}</p> {# #}
+ <p><strong>Size:</strong> {{+ type_layout_size|safe }}</p> {# #}
{% if !variants.is_empty() %}
<p> {# #}
<strong>Size for each variant:</strong> {# #}