summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html')
-rw-r--r--src/librustdoc/html/format.rs168
-rw-r--r--src/librustdoc/html/highlight.rs6
-rw-r--r--src/librustdoc/html/highlight/fixtures/sample.html8
-rw-r--r--src/librustdoc/html/highlight/tests.rs2
-rw-r--r--src/librustdoc/html/layout.rs25
-rw-r--r--src/librustdoc/html/render/context.rs33
-rw-r--r--src/librustdoc/html/render/mod.rs265
-rw-r--r--src/librustdoc/html/render/print_item.rs174
-rw-r--r--src/librustdoc/html/render/span_map.rs10
-rw-r--r--src/librustdoc/html/render/write_shared.rs290
-rw-r--r--src/librustdoc/html/sources.rs25
-rw-r--r--src/librustdoc/html/static/css/noscript.css6
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css526
-rw-r--r--src/librustdoc/html/static/css/settings.css57
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css126
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css122
-rw-r--r--src/librustdoc/html/static/css/themes/light.css116
-rw-r--r--src/librustdoc/html/static/fonts/README.txt12
-rw-r--r--src/librustdoc/html/static/js/main.js184
-rw-r--r--src/librustdoc/html/static/js/scrape-examples.js34
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--src/librustdoc/html/static/js/settings.js74
-rw-r--r--src/librustdoc/html/static/js/source-script.js11
-rw-r--r--src/librustdoc/html/static/js/storage.js24
-rw-r--r--src/librustdoc/html/static/scrape-examples-help.md5
-rw-r--r--src/librustdoc/html/static_files.rs263
-rw-r--r--src/librustdoc/html/templates/page.html57
-rw-r--r--src/librustdoc/html/templates/print_item.html8
28 files changed, 1193 insertions, 1459 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 92e7f2739..39e2a9022 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -107,10 +107,6 @@ impl Buffer {
self.buffer
}
- pub(crate) fn insert_str(&mut self, idx: usize, s: &str) {
- self.buffer.insert_str(idx, s);
- }
-
pub(crate) fn push_str(&mut self, s: &str) {
self.buffer.push_str(s);
}
@@ -659,7 +655,7 @@ pub(crate) fn href_with_root_path(
}
if !did.is_local()
- && !cache.effective_visibilities.is_directly_public(did)
+ && !cache.effective_visibilities.is_directly_public(tcx, did)
&& !cache.document_private
&& !cache.primitive_locations.values().any(|&id| id == did)
{
@@ -1232,9 +1228,8 @@ impl clean::Arguments {
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
for (i, input) in self.values.iter().enumerate() {
- if !input.name.is_empty() {
- write!(f, "{}: ", input.name)?;
- }
+ write!(f, "{}: ", input.name)?;
+
if f.alternate() {
write!(f, "{:#}", input.type_.print(cx))?;
} else {
@@ -1367,10 +1362,8 @@ impl clean::FnDecl {
args.push_str("const ");
args_plain.push_str("const ");
}
- if !input.name.is_empty() {
- write!(args, "{}: ", input.name);
- write!(args_plain, "{}: ", input.name);
- }
+ write!(args, "{}: ", input.name);
+ write!(args_plain, "{}: ", input.name);
if f.alternate() {
write!(args, "{:#}", input.type_.print(cx));
@@ -1420,87 +1413,84 @@ impl clean::FnDecl {
}
}
-impl clean::Visibility {
- pub(crate) fn print_with_space<'a, 'tcx: 'a>(
- self,
- item_did: ItemId,
- cx: &'a Context<'tcx>,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- use std::fmt::Write as _;
-
- let to_print: Cow<'static, str> = match self {
- clean::Public => "pub ".into(),
- clean::Inherited => "".into(),
- clean::Visibility::Restricted(vis_did) => {
- // FIXME(camelid): This may not work correctly if `item_did` is a module.
- // However, rustdoc currently never displays a module's
- // visibility, so it shouldn't matter.
- let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
-
- if vis_did.is_crate_root() {
- "pub(crate) ".into()
- } else if parent_module == Some(vis_did) {
- // `pub(in foo)` where `foo` is the parent module
- // is the same as no visibility modifier
- "".into()
- } else if parent_module
- .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
- == Some(vis_did)
- {
- "pub(super) ".into()
- } else {
- let path = cx.tcx().def_path(vis_did);
- debug!("path={:?}", path);
- // modified from `resolved_path()` to work with `DefPathData`
- let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
- let anchor = anchor(vis_did, last_name, cx).to_string();
-
- let mut s = "pub(in ".to_owned();
- for seg in &path.data[..path.data.len() - 1] {
- let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
- }
- let _ = write!(s, "{}) ", anchor);
- s.into()
+pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
+ visibility: Option<ty::Visibility<DefId>>,
+ item_did: ItemId,
+ cx: &'a Context<'tcx>,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+ use std::fmt::Write as _;
+
+ let to_print: Cow<'static, str> = match visibility {
+ None => "".into(),
+ Some(ty::Visibility::Public) => "pub ".into(),
+ Some(ty::Visibility::Restricted(vis_did)) => {
+ // FIXME(camelid): This may not work correctly if `item_did` is a module.
+ // However, rustdoc currently never displays a module's
+ // visibility, so it shouldn't matter.
+ let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
+
+ if vis_did.is_crate_root() {
+ "pub(crate) ".into()
+ } else if parent_module == Some(vis_did) {
+ // `pub(in foo)` where `foo` is the parent module
+ // is the same as no visibility modifier
+ "".into()
+ } else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
+ == Some(vis_did)
+ {
+ "pub(super) ".into()
+ } else {
+ let path = cx.tcx().def_path(vis_did);
+ debug!("path={:?}", path);
+ // modified from `resolved_path()` to work with `DefPathData`
+ let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
+ let anchor = anchor(vis_did, last_name, cx).to_string();
+
+ let mut s = "pub(in ".to_owned();
+ for seg in &path.data[..path.data.len() - 1] {
+ let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
}
+ let _ = write!(s, "{}) ", anchor);
+ s.into()
}
- };
- display_fn(move |f| write!(f, "{}", to_print))
- }
+ }
+ };
+ display_fn(move |f| write!(f, "{}", to_print))
+}
- /// This function is the same as print_with_space, except that it renders no links.
- /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
- /// any HTML in it.
- pub(crate) fn to_src_with_space<'a, 'tcx: 'a>(
- self,
- tcx: TyCtxt<'tcx>,
- item_did: DefId,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- let to_print = match self {
- clean::Public => "pub ".to_owned(),
- clean::Inherited => String::new(),
- clean::Visibility::Restricted(vis_did) => {
- // FIXME(camelid): This may not work correctly if `item_did` is a module.
- // However, rustdoc currently never displays a module's
- // visibility, so it shouldn't matter.
- let parent_module = find_nearest_parent_module(tcx, item_did);
-
- if vis_did.is_crate_root() {
- "pub(crate) ".to_owned()
- } else if parent_module == Some(vis_did) {
- // `pub(in foo)` where `foo` is the parent module
- // is the same as no visibility modifier
- String::new()
- } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
- == Some(vis_did)
- {
- "pub(super) ".to_owned()
- } else {
- format!("pub(in {}) ", tcx.def_path_str(vis_did))
- }
+/// This function is the same as print_with_space, except that it renders no links.
+/// It's used for macros' rendered source view, which is syntax highlighted and cannot have
+/// any HTML in it.
+pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
+ visibility: Option<ty::Visibility<DefId>>,
+ tcx: TyCtxt<'tcx>,
+ item_did: DefId,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+ let to_print = match visibility {
+ None => String::new(),
+ Some(ty::Visibility::Public) => "pub ".to_owned(),
+ Some(ty::Visibility::Restricted(vis_did)) => {
+ // FIXME(camelid): This may not work correctly if `item_did` is a module.
+ // However, rustdoc currently never displays a module's
+ // visibility, so it shouldn't matter.
+ let parent_module = find_nearest_parent_module(tcx, item_did);
+
+ if vis_did.is_crate_root() {
+ "pub(crate) ".to_owned()
+ } else if parent_module == Some(vis_did) {
+ // `pub(in foo)` where `foo` is the parent module
+ // is the same as no visibility modifier
+ String::new()
+ } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
+ == Some(vis_did)
+ {
+ "pub(super) ".to_owned()
+ } else {
+ format!("pub(in {}) ", tcx.def_path_str(vis_did))
}
- };
- display_fn(move |f| f.write_str(&to_print))
- }
+ }
+ };
+ display_fn(move |f| f.write_str(&to_print))
}
pub(crate) trait PrintWithSpace {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 5e28204b2..cd8c8c463 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -72,8 +72,12 @@ pub(crate) fn render_source_with_highlighting(
line_numbers: Buffer,
href_context: HrefContext<'_, '_, '_>,
decoration_info: DecorationInfo,
+ extra: Option<&str>,
) {
write_header(out, "", Some(line_numbers), Tooltip::None);
+ if let Some(extra) = extra {
+ out.push_str(extra);
+ }
write_code(out, src, Some(href_context), Some(decoration_info));
write_footer(out, None);
}
@@ -358,7 +362,7 @@ impl Class {
match self {
Class::Comment => "comment",
Class::DocComment => "doccomment",
- Class::Attribute => "attribute",
+ Class::Attribute => "attr",
Class::KeyWord => "kw",
Class::RefKeyWord => "kw-2",
Class::Self_(_) => "self",
diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html
index 4a5a3cf60..fced2eacd 100644
--- a/src/librustdoc/html/highlight/fixtures/sample.html
+++ b/src/librustdoc/html/highlight/fixtures/sample.html
@@ -3,16 +3,16 @@
.kw { color: #8959A8; }
.kw-2, .prelude-ty { color: #4271AE; }
.number, .string { color: #718C00; }
-.self, .bool-val, .prelude-val, .attribute, .attribute .ident { color: #C82829; }
+.self, .bool-val, .prelude-val, .attr, .attr .ident { color: #C82829; }
.macro, .macro-nonterminal { color: #3E999F; }
.lifetime { color: #B76514; }
.question-mark { color: #ff9011; }
</style>
-<pre><code><span class="attribute">#![crate_type = <span class="string">&quot;lib&quot;</span>]
+<pre><code><span class="attr">#![crate_type = <span class="string">&quot;lib&quot;</span>]
</span><span class="kw">use </span>std::path::{Path, PathBuf};
-<span class="attribute">#[cfg(target_os = <span class="string">&quot;linux&quot;</span>)]
+<span class="attr">#[cfg(target_os = <span class="string">&quot;linux&quot;</span>)]
#[cfg(target_os = <span class="string">&quot;windows&quot;</span>)]
</span><span class="kw">fn </span>main() -&gt; () {
<span class="kw">let </span>foo = <span class="bool-val">true </span>&amp;&amp; <span class="bool-val">false </span>|| <span class="bool-val">true</span>;
@@ -23,7 +23,7 @@
<span class="macro">mac!</span>(foo, <span class="kw-2">&amp;mut </span>bar);
<span class="macro">assert!</span>(<span class="self">self</span>.length &lt; N &amp;&amp; index &lt;= <span class="self">self</span>.length);
::std::env::var(<span class="string">&quot;gateau&quot;</span>).is_ok();
- <span class="attribute">#[rustfmt::skip]
+ <span class="attr">#[rustfmt::skip]
</span><span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new();
<span class="kw">let </span><span class="kw-2">mut </span>s = String::new();
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index a5e633df4..2c93b9a09 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -9,7 +9,7 @@ const STYLE: &str = r#"
.kw { color: #8959A8; }
.kw-2, .prelude-ty { color: #4271AE; }
.number, .string { color: #718C00; }
-.self, .bool-val, .prelude-val, .attribute, .attribute .ident { color: #C82829; }
+.self, .bool-val, .prelude-val, .attr, .attr .ident { color: #C82829; }
.macro, .macro-nonterminal { color: #3E999F; }
.lifetime { color: #B76514; }
.question-mark { color: #ff9011; }
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 7d6d4b71e..a60e7cb10 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -2,13 +2,14 @@ use std::path::PathBuf;
use rustc_data_structures::fx::FxHashMap;
-use crate::error::Error;
use crate::externalfiles::ExternalHtml;
use crate::html::format::{Buffer, Print};
use crate::html::render::{ensure_trailing_slash, StylePath};
use askama::Template;
+use super::static_files::{StaticFiles, STATIC_FILES};
+
#[derive(Clone)]
pub(crate) struct Layout {
pub(crate) logo: String,
@@ -34,17 +35,23 @@ pub(crate) struct Page<'a> {
}
impl<'a> Page<'a> {
- pub(crate) fn get_static_root_path(&self) -> &str {
- self.static_root_path.unwrap_or(self.root_path)
+ pub(crate) fn get_static_root_path(&self) -> String {
+ match self.static_root_path {
+ Some(s) => s.to_string(),
+ None => format!("{}static.files/", self.root_path),
+ }
}
}
#[derive(Template)]
#[template(path = "page.html")]
struct PageLayout<'a> {
- static_root_path: &'a str,
+ static_root_path: String,
page: &'a Page<'a>,
layout: &'a Layout,
+
+ files: &'static StaticFiles,
+
themes: Vec<String>,
sidebar: String,
content: String,
@@ -61,19 +68,17 @@ pub(crate) fn render<T: Print, S: Print>(
) -> String {
let static_root_path = page.get_static_root_path();
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
- let mut themes: Vec<String> = style_files
- .iter()
- .map(StylePath::basename)
- .collect::<Result<_, Error>>()
- .unwrap_or_default();
+ let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
themes.sort();
- let rustdoc_version = rustc_interface::util::version_str().unwrap_or("unknown version");
+
+ let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
PageLayout {
static_root_path,
page,
layout,
+ files: &STATIC_FILES,
themes,
sidebar,
content,
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5733d1f9c..73690c86f 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -32,7 +32,7 @@ use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
use crate::html::url_parts_builder::UrlPartsBuilder;
-use crate::html::{layout, sources};
+use crate::html::{layout, sources, static_files};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
@@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> {
/// the source files are present in the html rendering, then this will be
/// `true`.
pub(crate) include_sources: bool,
+ /// Collection of all types with notable traits referenced in the current module.
+ pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Context<'_>, 128);
+rustc_data_structures::static_assert_size!(Context<'_>, 160);
/// Shared mutable state used in [`Context`] and elsewhere.
pub(crate) struct SharedContext<'tcx> {
@@ -498,7 +500,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
);
let (sender, receiver) = channel();
- let mut scx = SharedContext {
+ let scx = SharedContext {
tcx,
src_root,
local_sources,
@@ -521,19 +523,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
call_locations,
};
- // Add the default themes to the `Vec` of stylepaths
- //
- // Note that these must be added before `sources::render` is called
- // so that the resulting source pages are styled
- //
- // `light.css` is not disabled because it is the stylesheet that stays loaded
- // by the browser as the theme stylesheet. The theme system (hackily) works by
- // changing the href to this stylesheet. All other themes are disabled to
- // prevent rule conflicts
- scx.style_files.push(StylePath { path: PathBuf::from("light.css") });
- scx.style_files.push(StylePath { path: PathBuf::from("dark.css") });
- scx.style_files.push(StylePath { path: PathBuf::from("ayu.css") });
-
let dst = output;
scx.ensure_dir(&dst)?;
@@ -545,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
deref_id_map: FxHashMap::default(),
shared: Rc::new(scx),
include_sources,
+ types_with_notable_traits: FxHashSet::default(),
};
if emit_crate {
@@ -573,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
id_map: IdMap::new(),
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
+ types_with_notable_traits: FxHashSet::default(),
}
}
@@ -647,10 +638,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</section>\
</noscript>\
<link rel=\"stylesheet\" type=\"text/css\" \
- href=\"{root_path}settings{suffix}.css\">\
- <script defer src=\"{root_path}settings{suffix}.js\"></script>",
- root_path = page.static_root_path.unwrap_or(""),
- suffix = page.resource_suffix,
+ href=\"{static_root_path}{settings_css}\">\
+ <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,
)
},
&shared.style_files,
@@ -815,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
}
}
+
Ok(())
}
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 96c57c8c8..36d15ec3b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -59,7 +59,7 @@ use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
};
-use serde::ser::SerializeSeq;
+use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Serialize, Serializer};
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
@@ -70,8 +70,8 @@ use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
- print_default_space, print_generic_bounds, print_where_clause, Buffer, Ending, HrefError,
- PrintWithSpace,
+ print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
+ Buffer, Ending, HrefError, PrintWithSpace,
};
use crate::html::highlight;
use crate::html::markdown::{
@@ -747,11 +747,12 @@ fn assoc_const(
extra: &str,
cx: &Context<'_>,
) {
+ let tcx = cx.tcx();
write!(
w,
"{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}",
extra = extra,
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(),
ty = ty.print(cx),
@@ -764,7 +765,7 @@ fn assoc_const(
// This hurts readability in this context especially when more complex expressions
// are involved and it doesn't add much of value.
// Find a way to print constants here without all that jazz.
- write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx()))));
+ write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
}
}
@@ -802,17 +803,18 @@ fn assoc_method(
d: &clean::FnDecl,
link: AssocItemLink<'_>,
parent: ItemType,
- cx: &Context<'_>,
+ cx: &mut Context<'_>,
render_mode: RenderMode,
) {
- let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item");
+ let tcx = cx.tcx();
+ let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
let name = meth.name.as_ref().unwrap();
- let vis = meth.visibility.print_with_space(meth.item_id, cx).to_string();
+ let vis = visibility_print_with_space(meth.visibility(tcx), meth.item_id, cx).to_string();
// FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
// this condition.
let constness = match render_mode {
RenderMode::Normal => {
- print_constness_with_space(&header.constness, meth.const_stability(cx.tcx()))
+ print_constness_with_space(&header.constness, meth.const_stability(tcx))
}
RenderMode::ForDeref { .. } => "",
};
@@ -834,6 +836,8 @@ fn assoc_method(
+ name.as_str().len()
+ generics_len;
+ let notable_traits = d.output.as_return().and_then(|output| notable_traits_button(output, cx));
+
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
@@ -843,10 +847,10 @@ fn assoc_method(
render_attributes_in_code(w, meth);
(0, "", Ending::Newline)
};
- w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
+ w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
write!(
w,
- "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fn\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
indent = indent_str,
vis = vis,
@@ -859,9 +863,9 @@ fn assoc_method(
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, cx),
- notable_traits = notable_traits_decl(d, cx),
+ notable_traits = notable_traits.unwrap_or_default(),
where_clause = print_where_clause(g, cx, indent, end_newline),
- )
+ );
}
/// Writes a span containing the versions at which an item became stable and/or const-stable. For
@@ -961,7 +965,7 @@ fn render_assoc_item(
item: &clean::Item,
link: AssocItemLink<'_>,
parent: ItemType,
- cx: &Context<'_>,
+ cx: &mut Context<'_>,
render_mode: RenderMode,
) {
match &*item.kind {
@@ -1067,7 +1071,7 @@ fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
w,
"<h2 id=\"{id}\" class=\"small-section-header\">\
{title}\
- <a href=\"#{id}\" class=\"anchor\"></a>\
+ <a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
);
}
@@ -1271,88 +1275,133 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
}
}
-fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
- let mut out = Buffer::html();
+pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> Option<String> {
+ let mut has_notable_trait = false;
+
+ let did = ty.def_id(cx.cache())?;
- if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t)))
+ // Box has pass-through impls for Read, Write, Iterator, and Future when the
+ // boxed type implements one of those. We don't want to treat every Box return
+ // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
+ // issue, with a pass-through impl for Future.
+ if Some(did) == cx.tcx().lang_items().owned_box()
+ || Some(did) == cx.tcx().lang_items().pin_type()
{
- // Box has pass-through impls for Read, Write, Iterator, and Future when the
- // boxed type implements one of those. We don't want to treat every Box return
- // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
- // issue, with a pass-through impl for Future.
- if Some(did) == cx.tcx().lang_items().owned_box()
- || Some(did) == cx.tcx().lang_items().pin_type()
- {
- return "".to_string();
- }
- if let Some(impls) = cx.cache().impls.get(&did) {
- for i in impls {
- let impl_ = i.inner_impl();
- if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache())
+ return None;
+ }
+
+ if let Some(impls) = cx.cache().impls.get(&did) {
+ for i in impls {
+ let impl_ = i.inner_impl();
+ if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ // Two different types might have the same did,
+ // without actually being the same.
+ continue;
+ }
+ if let Some(trait_) = &impl_.trait_ {
+ let trait_did = trait_.def_id();
+
+ if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
{
- // Two different types might have the same did,
- // without actually being the same.
- continue;
+ has_notable_trait = true;
}
- if let Some(trait_) = &impl_.trait_ {
- let trait_did = trait_.def_id();
-
- if cx
- .cache()
- .traits
- .get(&trait_did)
- .map_or(false, |t| t.is_notable_trait(cx.tcx()))
- {
- if out.is_empty() {
- write!(
- &mut out,
- "<span class=\"notable\">Notable traits for {}</span>\
- <code class=\"content\">",
- impl_.for_.print(cx)
- );
- }
+ }
+ }
+ }
- //use the "where" class here to make it small
- write!(
+ if has_notable_trait {
+ cx.types_with_notable_traits.insert(ty.clone());
+ Some(format!(
+ " <a href=\"#\" class=\"notable-traits\" data-ty=\"{ty}\">ⓘ</a>",
+ ty = Escape(&format!("{:#}", ty.print(cx))),
+ ))
+ } else {
+ None
+ }
+}
+
+fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
+ let mut out = Buffer::html();
+
+ let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
+
+ let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
+
+ for i in impls {
+ let impl_ = i.inner_impl();
+ if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ // Two different types might have the same did,
+ // without actually being the same.
+ continue;
+ }
+ if let Some(trait_) = &impl_.trait_ {
+ let trait_did = trait_.def_id();
+
+ if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) {
+ if out.is_empty() {
+ write!(
+ &mut out,
+ "<h3>Notable traits for <code>{}</code></h3>\
+ <pre class=\"content\"><code>",
+ impl_.for_.print(cx)
+ );
+ }
+
+ //use the "where" class here to make it small
+ write!(
+ &mut out,
+ "<span class=\"where fmt-newline\">{}</span>",
+ impl_.print(false, cx)
+ );
+ for it in &impl_.items {
+ if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
+ out.push_str("<span class=\"where fmt-newline\"> ");
+ let empty_set = FxHashSet::default();
+ let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
+ assoc_type(
&mut out,
- "<span class=\"where fmt-newline\">{}</span>",
- impl_.print(false, cx)
+ it,
+ &tydef.generics,
+ &[], // intentionally leaving out bounds
+ Some(&tydef.type_),
+ src_link,
+ 0,
+ cx,
);
- for it in &impl_.items {
- if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
- out.push_str("<span class=\"where fmt-newline\"> ");
- let empty_set = FxHashSet::default();
- let src_link =
- AssocItemLink::GotoSource(trait_did.into(), &empty_set);
- assoc_type(
- &mut out,
- it,
- &tydef.generics,
- &[], // intentionally leaving out bounds
- Some(&tydef.type_),
- src_link,
- 0,
- cx,
- );
- out.push_str(";</span>");
- }
- }
+ out.push_str(";</span>");
}
}
}
}
}
-
- if !out.is_empty() {
- out.insert_str(
- 0,
- "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
- <span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
- );
- out.push_str("</code></span></span></span></span>");
+ if out.is_empty() {
+ write!(&mut out, "</code></pre>",);
}
- out.into_inner()
+ (format!("{:#}", ty.print(cx)), out.into_inner())
+}
+
+pub(crate) fn notable_traits_json<'a>(
+ tys: impl Iterator<Item = &'a clean::Type>,
+ cx: &Context<'_>,
+) -> String {
+ let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
+ mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
+ struct NotableTraitsMap(Vec<(String, String)>);
+ impl Serialize for NotableTraitsMap {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut map = serializer.serialize_map(Some(self.0.len()))?;
+ for item in &self.0 {
+ map.serialize_entry(&item.0, &item.1)?;
+ }
+ map.end()
+ }
+ }
+ serde_json::to_string(&NotableTraitsMap(mp))
+ .expect("serialize (string, string) -> json object cannot fail")
}
#[derive(Clone, Copy, Debug)]
@@ -1487,7 +1536,7 @@ fn render_impl(
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
render_assoc_item(
@@ -1513,7 +1562,7 @@ fn render_impl(
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_const(
@@ -1538,7 +1587,7 @@ fn render_impl(
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_type(
@@ -1564,7 +1613,7 @@ fn render_impl(
);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_type(
@@ -1797,7 +1846,7 @@ pub(crate) fn render_impl_summary(
};
write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
write!(w, "<h3 class=\"code-header\">");
if let Some(use_absolute) = use_absolute {
@@ -2231,12 +2280,12 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea
let fields = get_struct_fields_name(&s.fields);
if !fields.is_empty() {
- match s.struct_type {
- CtorKind::Fictive => {
+ match s.ctor_kind {
+ None => {
print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
}
- CtorKind::Fn => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
- CtorKind::Const => {}
+ Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
+ Some(CtorKind::Const) => {}
}
}
@@ -2866,11 +2915,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
);
if line_ranges.len() > 1 {
- write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#);
- }
-
- if needs_expansion {
- write!(w, r#"<span class="expand">&varr;</span>"#);
+ write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#);
}
// Look for the example file in the source map if it exists, otherwise return a dummy span
@@ -2892,9 +2937,6 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
})()
.unwrap_or(rustc_span::DUMMY_SP);
- // The root path is the inverse of Context::current
- let root_path = vec!["../"; cx.current.len() - 1].join("");
-
let mut decoration_info = FxHashMap::default();
decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
decoration_info.insert("highlight", byte_ranges);
@@ -2904,9 +2946,9 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
contents_subset,
file_span,
cx,
- &root_path,
+ &cx.root_path(),
highlight::DecorationInfo(decoration_info),
- sources::SourceContext::Embedded { offset: line_min },
+ sources::SourceContext::Embedded { offset: line_min, needs_expansion },
);
write!(w, "</div></div>");
@@ -2915,14 +2957,23 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
// The call locations are output in sequence, so that sequence needs to be determined.
// Ideally the most "relevant" examples would be shown first, but there's no general algorithm
- // for determining relevance. Instead, we prefer the smallest examples being likely the easiest to
- // understand at a glance.
+ // for determining relevance. We instead proxy relevance with the following heuristics:
+ // 1. Code written to be an example is better than code not written to be an example, e.g.
+ // a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
+ // directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
+ // a --crate-type bin.
+ // 2. Smaller examples are better than large examples. So we prioritize snippets that have
+ // the smallest number of lines in their enclosing item.
+ // 3. Finally we sort by the displayed file name, which is arbitrary but prevents the
+ // ordering of examples from randomly changing between Rustdoc invocations.
let ordered_locations = {
- let sort_criterion = |(_, call_data): &(_, &CallData)| {
+ fn sort_criterion<'a>(
+ (_, call_data): &(&PathBuf, &'a CallData),
+ ) -> (bool, u32, &'a String) {
// Use the first location because that's what the user will see initially
let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
- hi - lo
- };
+ (!call_data.is_bin, hi - lo, &call_data.display_name)
+ }
let mut locs = call_locations.iter().collect::<Vec<_>>();
locs.sort_by_key(sort_criterion);
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 632781736..acbe3f228 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -7,19 +7,20 @@ use rustc_hir::def_id::DefId;
use rustc_middle::middle::stability;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::LayoutError;
-use rustc_middle::ty::{Adt, TyCtxt};
+use rustc_middle::ty::{self, Adt, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_target::abi::{Layout, Primitive, TagEncoding, Variants};
+use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants};
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
- item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
- render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
- render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
+ item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
+ render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
+ render_impl, render_rightside, render_stability_since_raw,
+ render_stability_since_raw_with_extra, AssocItemLink, Context, ImplRenderingParameters,
};
use crate::clean;
use crate::config::ModuleSorting;
@@ -28,12 +29,12 @@ use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
- Buffer, Ending, PrintWithSpace,
+ visibility_print_with_space, Buffer, Ending, PrintWithSpace,
};
-use crate::html::highlight;
use crate::html::layout::Page;
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
use crate::html::url_parts_builder::UrlPartsBuilder;
+use crate::html::{highlight, static_files};
use askama::Template;
use itertools::Itertools;
@@ -52,8 +53,8 @@ struct PathComponent {
#[derive(Template)]
#[template(path = "print_item.html")]
struct ItemVars<'a> {
- page: &'a Page<'a>,
static_root_path: &'a str,
+ clipboard_svg: &'static static_files::StaticFile,
typ: &'a str,
name: &'a str,
item_type: &'a str,
@@ -147,8 +148,8 @@ pub(super) fn print_item(
};
let item_vars = ItemVars {
- page,
- static_root_path: page.get_static_root_path(),
+ static_root_path: &page.get_static_root_path(),
+ clipboard_svg: &static_files::STATIC_FILES.clipboard_svg,
typ,
name: item.name.as_ref().unwrap().as_str(),
item_type: &item.type_().to_string(),
@@ -183,6 +184,16 @@ pub(super) fn print_item(
unreachable!();
}
}
+
+ // Render notable-traits.js used for all methods in this module.
+ if !cx.types_with_notable_traits.is_empty() {
+ write!(
+ buf,
+ r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
+ notable_traits_json(cx.types_with_notable_traits.iter(), cx)
+ );
+ cx.types_with_notable_traits.clear();
+ }
}
/// For large structs, enums, unions, etc, determine whether to hide their fields
@@ -318,6 +329,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
);
}
+ let tcx = cx.tcx();
match *myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::anchor;
@@ -327,14 +339,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
Some(src) => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {} as {};",
- myitem.visibility.print_with_space(myitem.item_id, cx),
+ visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
anchor(myitem.item_id.expect_def_id(), src, cx),
myitem.name.unwrap(),
),
None => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {};",
- myitem.visibility.print_with_space(myitem.item_id, cx),
+ visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx),
),
}
@@ -384,7 +396,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
</div>\
{stab_tags_before}{stab_tags}{stab_tags_after}",
stab = stab.unwrap_or_default(),
- vis = myitem.visibility.print_with_space(myitem.item_id, cx),
+ vis = visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
imp = import.print(cx),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
@@ -408,8 +420,8 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
let stab = myitem.stability_class(cx.tcx());
let add = if stab.is_some() { " " } else { "" };
- let visibility_emoji = match myitem.visibility {
- clean::Visibility::Restricted(_) => {
+ let visibility_emoji = match myitem.visibility(tcx) {
+ Some(ty::Visibility::Restricted(_)) => {
"<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
}
_ => "",
@@ -496,12 +508,13 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
}
fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) {
- let header = it.fn_header(cx.tcx()).expect("printing a function which isn't a function");
- let constness = print_constness_with_space(&header.constness, it.const_stability(cx.tcx()));
+ let tcx = cx.tcx();
+ let header = it.fn_header(tcx).expect("printing a function which isn't a function");
+ let constness = print_constness_with_space(&header.constness, it.const_stability(tcx));
let unsafety = header.unsafety.print_with_space();
let abi = print_abi_with_space(header.abi).to_string();
let asyncness = header.asyncness.print_with_space();
- let visibility = it.visibility.print_with_space(it.item_id, cx).to_string();
+ let visibility = visibility_print_with_space(it.visibility(tcx), it.item_id, cx).to_string();
let name = it.name.unwrap();
let generics_len = format!("{:#}", f.generics.print(cx)).len();
@@ -514,6 +527,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
+ name.as_str().len()
+ generics_len;
+ let notable_traits =
+ f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
+
wrap_into_item_decl(w, |w| {
wrap_item(w, "fn", |w| {
render_attributes_in_pre(w, it, "");
@@ -531,14 +547,15 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
decl = f.decl.full_print(header_len, 0, cx),
- notable_traits = notable_traits_decl(&f.decl, cx),
+ notable_traits = notable_traits.unwrap_or_default(),
);
});
});
- document(w, cx, it, None, HeadingOffset::H2)
+ document(w, cx, it, None, HeadingOffset::H2);
}
fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {
+ let tcx = cx.tcx();
let bounds = bounds(&t.bounds, false, cx);
let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::<Vec<_>>();
let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
@@ -549,8 +566,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let count_types = required_types.len() + provided_types.len();
let count_consts = required_consts.len() + provided_consts.len();
let count_methods = required_methods.len() + provided_methods.len();
- let must_implement_one_of_functions =
- cx.tcx().trait_def(t.def_id).must_implement_one_of.clone();
+ let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone();
// Output the trait definition
wrap_into_item_decl(w, |w| {
@@ -559,9 +575,9 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"{}{}{}trait {}{}{}",
- it.visibility.print_with_space(it.item_id, cx),
- t.unsafety(cx.tcx()).print_with_space(),
- if t.is_auto(cx.tcx()) { "auto " } else { "" },
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+ t.unsafety(tcx).print_with_space(),
+ if t.is_auto(tcx) { "auto " } else { "" },
it.name.unwrap(),
t.generics.print(cx),
bounds
@@ -701,7 +717,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"<h2 id=\"{0}\" class=\"small-section-header\">\
- {1}<a href=\"#{0}\" class=\"anchor\"></a>\
+ {1}<a href=\"#{0}\" class=\"anchor\">§</a>\
</h2>{2}",
id, title, extra_content
)
@@ -1020,7 +1036,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
let extern_crates = extern_crates
.into_iter()
- .map(|cnum| cx.shared.tcx.crate_name(cnum).to_string())
+ .map(|cnum| tcx.crate_name(cnum).to_string())
.collect::<Vec<_>>()
.join(",");
let (extern_before, extern_after) =
@@ -1084,7 +1100,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
wrap_item(w, "typedef", |w| {
render_attributes_in_pre(w, it, "");
- write!(w, "{}", it.visibility.print_with_space(it.item_id, cx));
+ write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx));
write!(
w,
"type {}{}{where_clause} = {type_};",
@@ -1131,7 +1147,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
write!(
w,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
- Fields<a href=\"#fields\" class=\"anchor\"></a>\
+ Fields<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>"
);
for (field, ty) in fields {
@@ -1140,7 +1156,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
write!(
w,
"<span id=\"{id}\" class=\"{shortty} small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{name}: {ty}</code>\
</span>",
id = id,
@@ -1173,6 +1189,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]
}
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_into_item_decl(w, |w| {
wrap_item(w, "enum", |w| {
@@ -1180,7 +1197,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"{}enum {}{}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
it.name.unwrap(),
e.generics.print(cx),
);
@@ -1215,7 +1232,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w,
v,
None,
- s.struct_type,
+ s.ctor_kind,
&s.fields,
" ",
false,
@@ -1245,35 +1262,35 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"<h2 id=\"variants\" class=\"variants small-section-header\">\
- Variants{}<a href=\"#variants\" class=\"anchor\"></a>\
+ Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
</h2>",
document_non_exhaustive_header(it)
);
document_non_exhaustive(w, it);
+ write!(w, "<div class=\"variants\">");
for variant in e.variants() {
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
- "<h3 id=\"{id}\" class=\"variant small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
- <code>{name}",
+ "<section id=\"{id}\" class=\"variant\">\
+ <a href=\"#{id}\" class=\"anchor\">§</a>",
id = id,
- 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());
if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind {
w.write_str("(");
print_tuple_struct_fields(w, cx, s);
w.write_str(")");
}
- w.write_str("</code>");
- render_stability_since_raw(
- w,
- variant.stable_since(cx.tcx()),
- variant.const_stability(cx.tcx()),
- it.stable_since(cx.tcx()),
- it.const_stable_since(cx.tcx()),
- );
- w.write_str("</h3>");
+ w.write_str("</h3></section>");
use crate::clean::Variant;
@@ -1307,8 +1324,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"<div class=\"sub-variant-field\">\
- <span id=\"{id}\" class=\"variant small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <span id=\"{id}\" class=\"small-section-header\">\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}:&nbsp;{t}</code>\
</span>",
id = id,
@@ -1326,6 +1343,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
document(w, cx, variant, Some(it), HeadingOffset::H4);
}
+ write!(w, "</div>");
}
let def_id = it.item_id.expect_def_id();
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
@@ -1389,12 +1407,13 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_into_item_decl(w, |w| {
wrap_item(w, "const", |w| {
+ let tcx = cx.tcx();
render_attributes_in_code(w, it);
write!(
w,
"{vis}const {name}: {typ}",
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
name = it.name.unwrap(),
typ = c.type_.print(cx),
);
@@ -1408,9 +1427,9 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
// ` = 100i32;`
// instead?
- let value = c.value(cx.tcx());
- let is_literal = c.is_literal(cx.tcx());
- let expr = c.expr(cx.tcx());
+ let value = c.value(tcx);
+ let is_literal = c.is_literal(tcx);
+ let expr = c.expr(tcx);
if value.is_some() || is_literal {
write!(w, " = {expr};", expr = Escape(&expr));
} else {
@@ -1439,7 +1458,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
wrap_into_item_decl(w, |w| {
wrap_item(w, "struct", |w| {
render_attributes_in_code(w, it);
- render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
+ render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
});
@@ -1453,14 +1472,14 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
_ => None,
})
.peekable();
- if let CtorKind::Fictive | CtorKind::Fn = s.struct_type {
+ if let None | Some(CtorKind::Fn) = s.ctor_kind {
if fields.peek().is_some() {
write!(
w,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
- {}{}<a href=\"#fields\" class=\"anchor\"></a>\
+ {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>",
- if let CtorKind::Fictive = s.struct_type { "Fields" } else { "Tuple Fields" },
+ if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it)
);
document_non_exhaustive(w, it);
@@ -1471,7 +1490,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(
w,
"<span id=\"{id}\" class=\"{item_type} small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{name}: {ty}</code>\
</span>",
item_type = ItemType::StructField,
@@ -1495,7 +1514,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(
w,
"{vis}static {mutability}{name}: {typ}",
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
mutability = s.mutability.print_with_space(),
name = it.name.unwrap(),
typ = s.type_.print(cx)
@@ -1513,7 +1532,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
write!(
w,
" {}type {};\n}}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
it.name.unwrap(),
);
});
@@ -1666,7 +1685,13 @@ fn render_union(
tab: &str,
cx: &Context<'_>,
) {
- write!(w, "{}union {}", it.visibility.print_with_space(it.item_id, cx), it.name.unwrap(),);
+ let tcx = cx.tcx();
+ write!(
+ w,
+ "{}union {}",
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+ it.name.unwrap(),
+ );
let where_displayed = g
.map(|g| {
@@ -1693,7 +1718,7 @@ fn render_union(
write!(
w,
" {}{}: {},\n{}",
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
field.name.unwrap(),
ty.print(cx),
tab
@@ -1714,16 +1739,17 @@ fn render_struct(
w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
- ty: CtorKind,
+ ty: Option<CtorKind>,
fields: &[clean::Item],
tab: &str,
structhead: bool,
cx: &Context<'_>,
) {
+ let tcx = cx.tcx();
write!(
w,
"{}{}{}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
if structhead { "struct " } else { "" },
it.name.unwrap()
);
@@ -1731,7 +1757,7 @@ fn render_struct(
write!(w, "{}", g.print(cx))
}
match ty {
- CtorKind::Fictive => {
+ None => {
let where_diplayed = g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false);
// If there wasn't a `where` clause, we add a whitespace.
@@ -1753,7 +1779,7 @@ fn render_struct(
w,
"\n{} {}{}: {},",
tab,
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
field.name.unwrap(),
ty.print(cx),
);
@@ -1773,7 +1799,7 @@ fn render_struct(
}
w.write_str("}");
}
- CtorKind::Fn => {
+ Some(CtorKind::Fn) => {
w.write_str("(");
for (i, field) in fields.iter().enumerate() {
if i > 0 {
@@ -1785,7 +1811,7 @@ fn render_struct(
write!(
w,
"{}{}",
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
ty.print(cx),
)
}
@@ -1801,7 +1827,7 @@ fn render_struct(
w.write_str(";");
}
}
- CtorKind::Const => {
+ Some(CtorKind::Const) => {
// Needed for PhantomData.
if let Some(g) = g {
write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline));
@@ -1866,11 +1892,11 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
}
fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
- fn write_size_of_layout(w: &mut Buffer, layout: Layout<'_>, tag_size: u64) {
- if layout.abi().is_unsized() {
+ fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS<VariantIdx>, tag_size: u64) {
+ if layout.abi.is_unsized() {
write!(w, "(unsized)");
} else {
- let bytes = layout.size().bytes() - tag_size;
+ let bytes = layout.size.bytes() - tag_size;
write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },);
}
}
@@ -1882,7 +1908,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
writeln!(
w,
"<h2 id=\"layout\" class=\"small-section-header\"> \
- Layout<a href=\"#layout\" class=\"anchor\"></a></h2>"
+ Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
);
writeln!(w, "<div class=\"docblock\">");
@@ -1901,7 +1927,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
chapter for details on type layout guarantees.</p></div>"
);
w.write_str("<p><strong>Size:</strong> ");
- write_size_of_layout(w, ty_layout.layout, 0);
+ write_size_of_layout(w, &ty_layout.layout.0, 0);
writeln!(w, "</p>");
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
&ty_layout.layout.variants()
@@ -1927,7 +1953,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
for (index, layout) in variants.iter_enumerated() {
let name = adt.variant(index).name;
write!(w, "<li><code>{name}</code>: ", name = name);
- write_size_of_layout(w, *layout, tag_size);
+ write_size_of_layout(w, layout, tag_size);
writeln!(w, "</li>");
}
w.write_str("</ul>");
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 151ec2b28..4514894ca 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -140,7 +140,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
self.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
+ fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
if self.handle_macro(path.span) {
return;
}
@@ -190,12 +190,4 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
}
intravisit::walk_expr(self, expr);
}
-
- fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
- if self.handle_macro(path.span) {
- return;
- }
- self.handle_path(path);
- intravisit::walk_use(self, path, id);
- }
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 85f63c985..94d8a9fec 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -1,10 +1,8 @@
-use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
-use std::path::{Component, Path, PathBuf};
+use std::path::{Component, Path};
use std::rc::Rc;
-use std::sync::LazyLock as Lazy;
use itertools::Itertools;
use rustc_data_structures::flock;
@@ -20,123 +18,20 @@ use crate::error::Error;
use crate::html::{layout, static_files};
use crate::{try_err, try_none};
-static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
- map! {
- "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR,
- "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM,
- "FiraSans-LICENSE.txt" => static_files::fira_sans::LICENSE,
- "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR,
- "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD,
- "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC,
- "SourceSerif4-LICENSE.md" => static_files::source_serif_4::LICENSE,
- "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR,
- "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD,
- "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC,
- "SourceCodePro-LICENSE.txt" => static_files::source_code_pro::LICENSE,
- "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR,
- "NanumBarunGothic-LICENSE.txt" => static_files::nanum_barun_gothic::LICENSE,
- "LICENSE-MIT.txt" => static_files::LICENSE_MIT,
- "LICENSE-APACHE.txt" => static_files::LICENSE_APACHE,
- "COPYRIGHT.txt" => static_files::COPYRIGHT,
- }
-});
-
-enum SharedResource<'a> {
- /// This file will never change, no matter what toolchain is used to build it.
- ///
- /// It does not have a resource suffix.
- Unversioned { name: &'static str },
- /// This file may change depending on the toolchain.
- ///
- /// It has a resource suffix.
- ToolchainSpecific { basename: &'static str },
- /// This file may change for any crate within a build, or based on the CLI arguments.
- ///
- /// This differs from normal invocation-specific files because it has a resource suffix.
- InvocationSpecific { basename: &'a str },
-}
-
-impl SharedResource<'_> {
- fn extension(&self) -> Option<&OsStr> {
- use SharedResource::*;
- match self {
- Unversioned { name }
- | ToolchainSpecific { basename: name }
- | InvocationSpecific { basename: name } => Path::new(name).extension(),
- }
- }
-
- fn path(&self, cx: &Context<'_>) -> PathBuf {
- match self {
- SharedResource::Unversioned { name } => cx.dst.join(name),
- SharedResource::ToolchainSpecific { basename } => cx.suffix_path(basename),
- SharedResource::InvocationSpecific { basename } => cx.suffix_path(basename),
- }
- }
-
- fn should_emit(&self, emit: &[EmitType]) -> bool {
- if emit.is_empty() {
- return true;
- }
- let kind = match self {
- SharedResource::Unversioned { .. } => EmitType::Unversioned,
- SharedResource::ToolchainSpecific { .. } => EmitType::Toolchain,
- SharedResource::InvocationSpecific { .. } => EmitType::InvocationSpecific,
- };
- emit.contains(&kind)
- }
-}
-
-impl Context<'_> {
- fn suffix_path(&self, filename: &str) -> PathBuf {
- // We use splitn vs Path::extension here because we might get a filename
- // like `style.min.css` and we want to process that into
- // `style-suffix.min.css`. Path::extension would just return `css`
- // which would result in `style.min-suffix.css` which isn't what we
- // want.
- let (base, ext) = filename.split_once('.').unwrap();
- let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
- self.dst.join(&filename)
- }
-
- fn write_shared(
- &self,
- resource: SharedResource<'_>,
- contents: impl 'static + Send + AsRef<[u8]>,
- emit: &[EmitType],
- ) -> Result<(), Error> {
- if resource.should_emit(emit) {
- self.shared.fs.write(resource.path(self), contents)
- } else {
- Ok(())
- }
- }
-
- fn write_minify(
- &self,
- resource: SharedResource<'_>,
- contents: impl 'static + Send + AsRef<str> + AsRef<[u8]>,
- minify: bool,
- emit: &[EmitType],
- ) -> Result<(), Error> {
- if minify {
- let contents = contents.as_ref();
- let contents = if resource.extension() == Some(OsStr::new("css")) {
- minifier::css::minify(contents)
- .map_err(|e| {
- Error::new(format!("failed to minify CSS file: {}", e), resource.path(self))
- })?
- .to_string()
- } else {
- minifier::js::minify(contents).to_string()
- };
- self.write_shared(resource, contents, emit)
- } else {
- self.write_shared(resource, contents, emit)
- }
- }
-}
-
+/// Rustdoc writes out two kinds of shared files:
+/// - Static files, which are embedded in the rustdoc binary and are written with a
+/// filename that includes a hash of their contents. These will always have a new
+/// URL if the contents change, so they are safe to cache with the
+/// `Cache-Control: immutable` directive. They are written under the static.files/
+/// directory and are written when --emit-type is empty (default) or contains
+/// "toolchain-specific". If using the --static-root-path flag, it should point
+/// to a URL path prefix where each of these filenames can be fetched.
+/// - Invocation specific files. These are generated based on the crate(s) being
+/// documented. Their filenames need to be predictable without knowing their
+/// contents, so they do not include a hash in their filename and are not safe to
+/// cache with `Cache-Control: immutable`. They include the contents of the
+/// --resource-suffix flag and are emitted when --emit-type is empty (default)
+/// or contains "invocation-specific".
pub(super) fn write_shared(
cx: &mut Context<'_>,
krate: &Crate,
@@ -149,139 +44,52 @@ pub(super) fn write_shared(
let lock_file = cx.dst.join(".lock");
let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
- // Minified resources are usually toolchain resources. If they're not, they should use `cx.write_minify` directly.
- fn write_minify(
- basename: &'static str,
- contents: impl 'static + Send + AsRef<str> + AsRef<[u8]>,
- cx: &Context<'_>,
- options: &RenderOptions,
- ) -> Result<(), Error> {
- cx.write_minify(
- SharedResource::ToolchainSpecific { basename },
- contents,
- options.enable_minification,
- &options.emit,
- )
- }
-
- // Toolchain resources should never be dynamic.
- let write_toolchain = |p: &'static _, c: &'static _| {
- cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c, &options.emit)
- };
-
- // Crate resources should always be dynamic.
- let write_crate = |p: &_, make_content: &dyn Fn() -> Result<Vec<u8>, Error>| {
+ // InvocationSpecific resources should always be dynamic.
+ let write_invocation_specific = |p: &str, make_content: &dyn Fn() -> Result<Vec<u8>, Error>| {
let content = make_content()?;
- cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit)
+ if options.emit.is_empty() || options.emit.contains(&EmitType::InvocationSpecific) {
+ let output_filename = static_files::suffix_path(p, &cx.shared.resource_suffix);
+ cx.shared.fs.write(cx.dst.join(output_filename), content)
+ } else {
+ Ok(())
+ }
};
- // Given "foo.svg", return e.g. "url(\"foo1.58.0.svg\")"
- fn ver_url(cx: &Context<'_>, basename: &'static str) -> String {
- format!(
- "url(\"{}\")",
- SharedResource::ToolchainSpecific { basename }
- .path(cx)
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- )
- }
-
- // We use the AUTOREPLACE mechanism to inject into our static JS and CSS certain
- // values that are only known at doc build time. Since this mechanism is somewhat
- // surprising when reading the code, please limit it to rustdoc.css.
- write_minify(
- "rustdoc.css",
- static_files::RUSTDOC_CSS
- .replace(
- "/* AUTOREPLACE: */url(\"toggle-minus.svg\")",
- &ver_url(cx, "toggle-minus.svg"),
- )
- .replace("/* AUTOREPLACE: */url(\"toggle-plus.svg\")", &ver_url(cx, "toggle-plus.svg"))
- .replace("/* AUTOREPLACE: */url(\"down-arrow.svg\")", &ver_url(cx, "down-arrow.svg")),
- cx,
- options,
- )?;
-
- // Add all the static files. These may already exist, but we just
- // overwrite them anyway to make sure that they're fresh and up-to-date.
- write_minify("settings.css", static_files::SETTINGS_CSS, cx, options)?;
- write_minify("noscript.css", static_files::NOSCRIPT_CSS, cx, options)?;
-
- // To avoid "light.css" to be overwritten, we'll first run over the received themes and only
- // then we'll run over the "official" styles.
- let mut themes: FxHashSet<String> = FxHashSet::default();
+ cx.shared
+ .fs
+ .create_dir_all(cx.dst.join("static.files"))
+ .map_err(|e| PathError::new(e, "static.files"))?;
+ // Handle added third-party themes
for entry in &cx.shared.style_files {
let theme = entry.basename()?;
let extension =
try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
- // Handle the official themes
- match theme.as_str() {
- "light" => write_minify("light.css", static_files::themes::LIGHT, cx, options)?,
- "dark" => write_minify("dark.css", static_files::themes::DARK, cx, options)?,
- "ayu" => write_minify("ayu.css", static_files::themes::AYU, cx, options)?,
- _ => {
- // Handle added third-party themes
- let filename = format!("{}.{}", theme, extension);
- write_crate(&filename, &|| Ok(try_err!(fs::read(&entry.path), &entry.path)))?;
- }
- };
-
- themes.insert(theme.to_owned());
- }
-
- if (*cx.shared).layout.logo.is_empty() {
- write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?;
- }
- if (*cx.shared).layout.favicon.is_empty() {
- write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?;
- write_toolchain("favicon-16x16.png", static_files::RUST_FAVICON_PNG_16)?;
- write_toolchain("favicon-32x32.png", static_files::RUST_FAVICON_PNG_32)?;
- }
- write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
- write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
- write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
- write_toolchain("toggle-minus.svg", static_files::TOGGLE_MINUS_PNG)?;
- write_toolchain("toggle-plus.svg", static_files::TOGGLE_PLUS_PNG)?;
-
- let mut themes: Vec<&String> = themes.iter().collect();
- themes.sort();
-
- write_minify("main.js", static_files::MAIN_JS, cx, options)?;
- write_minify("search.js", static_files::SEARCH_JS, cx, options)?;
- write_minify("settings.js", static_files::SETTINGS_JS, cx, options)?;
-
- if cx.include_sources {
- write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT, cx, options)?;
- }
-
- write_minify("storage.js", static_files::STORAGE_JS, cx, options)?;
+ // Skip the official themes. They are written below as part of STATIC_FILES_LIST.
+ if matches!(theme.as_str(), "light" | "dark" | "ayu") {
+ continue;
+ }
- if cx.shared.layout.scrape_examples_extension {
- cx.write_minify(
- SharedResource::InvocationSpecific { basename: "scrape-examples.js" },
- static_files::SCRAPE_EXAMPLES_JS,
- options.enable_minification,
- &options.emit,
- )?;
+ let bytes = try_err!(fs::read(&entry.path), &entry.path);
+ let filename = format!("{}{}.{}", theme, cx.shared.resource_suffix, extension);
+ cx.shared.fs.write(cx.dst.join(filename), bytes)?;
}
+ // When the user adds their own CSS files with --extend-css, we write that as an
+ // invocation-specific file (that is, with a resource suffix).
if let Some(ref css) = cx.shared.layout.css_file_extension {
let buffer = try_err!(fs::read_to_string(css), css);
- // This varies based on the invocation, so it can't go through the write_minify wrapper.
- cx.write_minify(
- SharedResource::InvocationSpecific { basename: "theme.css" },
- buffer,
- options.enable_minification,
- &options.emit,
- )?;
+ let path = static_files::suffix_path("theme.css", &cx.shared.resource_suffix);
+ cx.shared.fs.write(cx.dst.join(path), buffer)?;
}
- write_minify("normalize.css", static_files::NORMALIZE_CSS, cx, options)?;
- for (name, contents) in &*FILES_UNVERSIONED {
- cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?;
+
+ if options.emit.is_empty() || options.emit.contains(&EmitType::Toolchain) {
+ let static_dir = cx.dst.join(Path::new("static.files"));
+ static_files::for_each(|f: &static_files::StaticFile| {
+ let filename = static_dir.join(f.output_filename());
+ cx.shared.fs.write(filename, f.minified())
+ })?;
}
/// Read a file and return all lines that match the `"{crate}":{data},` format,
@@ -463,7 +271,7 @@ pub(super) fn write_shared(
v.push_str("\\\n}');\ncreateSourceSidebar();\n");
Ok(v.into_bytes())
};
- write_crate("source-files.js", &make_sources)?;
+ write_invocation_specific("source-files.js", &make_sources)?;
}
// Update the search index and crate list.
@@ -477,7 +285,7 @@ pub(super) fn write_shared(
// Sort the indexes by crate so the file will be generated identically even
// with rustdoc running in parallel.
all_indexes.sort();
- write_crate("search-index.js", &|| {
+ write_invocation_specific("search-index.js", &|| {
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
v.push_str(&all_indexes.join(",\\\n"));
v.push_str(
@@ -490,7 +298,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
Ok(v.into_bytes())
})?;
- write_crate("crates.js", &|| {
+ write_invocation_specific("crates.js", &|| {
let krates = krates.iter().map(|k| format!("\"{}\"", k)).join(",");
Ok(format!("window.ALL_CRATES = [{}];", krates).into_bytes())
})?;
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 7ab65bff3..54e296959 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -258,7 +258,7 @@ where
pub(crate) enum SourceContext {
Standalone,
- Embedded { offset: usize },
+ Embedded { offset: usize, needs_expansion: bool },
}
/// Wrapper struct to render the source code of a file. This will do things like
@@ -274,28 +274,37 @@ pub(crate) fn print_src(
) {
let lines = s.lines().count();
let mut line_numbers = Buffer::empty_from(buf);
+ let extra;
line_numbers.write_str("<pre class=\"src-line-numbers\">");
+ let current_href = &context
+ .href_from_span(clean::Span::new(file_span), false)
+ .expect("only local crates should have sources emitted");
match source_context {
SourceContext::Standalone => {
+ extra = None;
for line in 1..=lines {
- writeln!(line_numbers, "<span id=\"{0}\">{0}</span>", line)
+ writeln!(line_numbers, "<a href=\"#{line}\" id=\"{line}\">{line}</a>")
}
}
- SourceContext::Embedded { offset } => {
- for line in 1..=lines {
- writeln!(line_numbers, "<span>{0}</span>", line + offset)
+ SourceContext::Embedded { offset, needs_expansion } => {
+ extra = if needs_expansion {
+ Some(r#"<button class="expand">&varr;</button>"#)
+ } else {
+ None
+ };
+ for line_number in 1..=lines {
+ let line = line_number + offset;
+ writeln!(line_numbers, "<span>{line}</span>")
}
}
}
line_numbers.write_str("</pre>");
- let current_href = &context
- .href_from_span(clean::Span::new(file_span), false)
- .expect("only local crates should have sources emitted");
highlight::render_source_with_highlighting(
s,
buf,
line_numbers,
highlight::HrefContext { context, file_span, root_path, current_href },
decoration_info,
+ extra,
);
}
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 301f03a16..54e8b6561 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -22,3 +22,9 @@ nav.sub {
.source .sidebar {
display: none;
}
+
+.notable-traits {
+ /* layout requires javascript
+ https://github.com/rust-lang/rust/issues/102576 */
+ display: none;
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 1cc954a98..afcb40224 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -4,7 +4,7 @@
font-style: normal;
font-weight: 400;
src: local('Fira Sans'),
- url("FiraSans-Regular.woff2") format("woff2");
+ url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -12,7 +12,7 @@
font-style: normal;
font-weight: 500;
src: local('Fira Sans Medium'),
- url("FiraSans-Medium.woff2") format("woff2");
+ url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");
font-display: swap;
}
@@ -22,7 +22,7 @@
font-style: normal;
font-weight: 400;
src: local('Source Serif 4'),
- url("SourceSerif4-Regular.ttf.woff2") format("woff2");
+ url("SourceSerif4-Regular-1f7d512b176f0f72.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -30,7 +30,7 @@
font-style: italic;
font-weight: 400;
src: local('Source Serif 4 Italic'),
- url("SourceSerif4-It.ttf.woff2") format("woff2");
+ url("SourceSerif4-It-d034fe4ef9d0fa00.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -38,7 +38,7 @@
font-style: normal;
font-weight: 700;
src: local('Source Serif 4 Bold'),
- url("SourceSerif4-Bold.ttf.woff2") format("woff2");
+ url("SourceSerif4-Bold-124a1ca42af929b6.ttf.woff2") format("woff2");
font-display: swap;
}
@@ -49,28 +49,28 @@
font-weight: 400;
/* Avoid using locally installed font because bad versions are in circulation:
* see https://github.com/rust-lang/rust/issues/24355 */
- src: url("SourceCodePro-Regular.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: italic;
font-weight: 400;
- src: url("SourceCodePro-It.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 600;
- src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");
font-display: swap;
}
/* Avoid using legacy CJK serif fonts in Windows like Batang. */
@font-face {
font-family: 'NanumBarunGothic';
- src: url("NanumBarunGothic.ttf.woff2") format("woff2");
+ src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");
font-display: swap;
unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
}
@@ -159,13 +159,9 @@ h1.fqn {
.main-heading {
display: flex;
flex-wrap: wrap;
- justify-content: space-between;
padding-bottom: 6px;
margin-bottom: 15px;
}
-#toggle-all-docs {
- text-decoration: none;
-}
/* The only headings that get underlines are:
Markdown-generated headings within the top-doc
Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc)
@@ -199,17 +195,14 @@ h1, h2, h3, h4, h5, h6,
span.since,
a.srclink,
#help-button > a,
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.non-exhaustive > summary,
-.scraped-example-title,
-.more-examples-toggle summary, .more-examples-toggle .hide-more,
-.example-links a,
+summary.hideme,
+.scraped-example-list,
/* This selector is for the items listed in the "all items" page. */
ul.all-items {
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
}
-a#toggle-all-docs,
+#toggle-all-docs,
a.anchor,
.small-section-header a,
#source-sidebar a,
@@ -219,12 +212,8 @@ pre.rust a,
.mobile-topbar h2 a,
h1 a,
.search-results a,
-.module-item .stab,
-.import-item .stab,
-.result-name .primitive > i, .result-name .keyword > i,
-.method .where,
-.fn .where,
-.where.fmt-newline {
+.item-left .stab,
+.result-name .primitive > i, .result-name .keyword > i {
color: var(--main-color);
}
@@ -249,7 +238,6 @@ h1 a,
}
.content span.fn, .content a.fn,
-.content .fnname,
.content span.method, .content a.method,
.content span.tymethod, .content a.tymethod {
color: var(--function-link-color);
@@ -297,10 +285,21 @@ p:last-child {
button {
/* Buttons on Safari have different default padding than other platforms. Make them the same. */
padding: 1px 6px;
+ /* Opinionated tweak: use pointer cursor as clickability signifier. */
+ cursor: pointer;
}
/* end tweaks for normalize.css 8 */
+button#toggle-all-docs {
+ padding: 0;
+ background: none;
+ border: none;
+ /* iOS button gradient: https://stackoverflow.com/q/5438567 */
+ -webkit-appearance: none;
+ opacity: 1;
+}
+
.rustdoc {
display: flex;
flex-direction: row;
@@ -311,7 +310,7 @@ main {
position: relative;
flex-grow: 1;
padding: 10px 15px 40px 45px;
- min-width: 0;
+ min-width: 0; /* avoid growing beyond the size limit */
}
.source main {
@@ -360,7 +359,8 @@ img {
overflow: visible;
}
-.sub-logo-container {
+.sub-logo-container, .logo-container {
+ /* zero text boxes so that computed line height = image height exactly */
line-height: 0;
}
@@ -370,14 +370,17 @@ img {
object-fit: contain;
}
+.rust-logo {
+ filter: var(--rust-logo-filter);
+}
+
.sidebar, .mobile-topbar, .sidebar-menu-toggle {
background-color: var(--sidebar-background-color);
}
.sidebar {
font-size: 0.875rem;
- width: 200px;
- min-width: 200px;
+ flex: 0 0 200px;
overflow-y: scroll;
position: sticky;
height: 100vh;
@@ -386,12 +389,7 @@ img {
}
.rustdoc.source .sidebar {
- width: 50px;
- min-width: 0px;
- max-width: 300px;
- flex-grow: 0;
- flex-shrink: 0;
- flex-basis: auto;
+ flex-basis: 50px;
border-right: 1px solid;
overflow-x: hidden;
/* The sidebar is by default hidden */
@@ -412,7 +410,7 @@ img {
.source-sidebar-expanded .source .sidebar {
overflow-y: auto;
- width: 300px;
+ flex-basis: 300px;
}
.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) {
@@ -458,10 +456,9 @@ img {
}
.sidebar .logo-container {
- display: flex;
margin-top: 10px;
margin-bottom: 10px;
- justify-content: center;
+ text-align: center;
}
.version {
@@ -479,23 +476,17 @@ ul.block, .block li {
list-style: none;
}
-.block a,
-.sidebar h2 a,
-.sidebar h3 a {
+.sidebar-elems a,
+.sidebar > h2 a {
display: block;
- padding: 0.25rem;
+ padding: 0.25rem; /* 4px */
margin-left: -0.25rem;
-
- text-overflow: ellipsis;
- overflow: hidden;
}
.sidebar h2 {
overflow-wrap: anywhere;
padding: 0;
- margin: 0;
- margin-top: 0.7rem;
- margin-bottom: 0.7rem;
+ margin: 0.7rem 0;
}
.sidebar h3 {
@@ -523,6 +514,8 @@ ul.block, .block li {
.sidebar-elems .block li a {
white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.mobile-topbar {
@@ -570,15 +563,16 @@ ul.block, .block li {
border-color: var(--example-line-numbers-border-color);
}
-.src-line-numbers span {
- cursor: pointer;
+.src-line-numbers a, .src-line-numbers span {
color: var(--src-line-numbers-span-color);
}
-.src-line-numbers .line-highlighted {
- background-color: var(--src-line-number-highlighted-background-color);
-}
.src-line-numbers :target {
background-color: transparent;
+ border-right: none;
+ padding-right: 0;
+}
+.src-line-numbers .line-highlighted {
+ background-color: var(--src-line-number-highlighted-background-color);
}
.search-loading {
@@ -636,22 +630,16 @@ pre, .rustdoc.source .example-wrap {
.docblock table {
margin: .5em 0;
- width: calc(100% - 2px);
- overflow-x: auto;
- display: block;
border-collapse: collapse;
}
-.docblock table td {
+.docblock table td, .docblock table th {
padding: .5em;
- border: 1px dashed var(--border-color);
- vertical-align: top;
+ border: 1px solid var(--border-color);
}
-.docblock table th {
- padding: .5em;
- text-align: left;
- border: 1px solid var(--border-color);
+.docblock table tbody tr:nth-child(2n) {
+ background: var(--table-alt-row-background-color);
}
/* Shift "where ..." part of method or fn definition down a line */
@@ -672,7 +660,6 @@ pre, .rustdoc.source .example-wrap {
}
#main-content > .item-info {
- margin-top: 0;
margin-left: 0;
}
@@ -701,8 +688,8 @@ a {
}
.small-section-header {
- display: flex;
- justify-content: space-between;
+ /* fields use <span> tags, but should get their own lines */
+ display: block;
position: relative;
}
@@ -710,7 +697,7 @@ a {
display: initial;
}
-.impl:hover > .anchor, .trait-impl:hover > .anchor {
+.impl:hover > .anchor, .trait-impl:hover > .anchor, .variant:hover > .anchor {
display: inline-block;
position: absolute;
}
@@ -730,9 +717,6 @@ a {
h2.small-section-header > .anchor {
padding-right: 6px;
}
-.anchor::before {
- content: '§';
-}
.main-heading a:hover,
.example-wrap > pre.rust a:hover,
@@ -787,14 +771,12 @@ table,
margin-top: 0;
white-space: nowrap;
/* flex layout allows shrinking the <select> appropriately if it becomes too large */
- display: inline-flex;
- max-width: 100%;
+ display: flex;
/* make things look like in a line, despite the fact that we're using a layout
with boxes (i.e. from the flex layout) */
align-items: baseline;
}
#crate-search-div {
- display: inline-block;
/* ensures that 100% in properties of #crate-search-div:after
are relative to the size of this div */
position: relative;
@@ -803,10 +785,8 @@ table,
}
#crate-search {
min-width: 115px;
- padding: 0;
/* keep these two in sync with "@-moz-document url-prefix()" below */
- padding-left: 4px;
- padding-right: 23px;
+ padding: 0 23px 0 4px;
/* prevents the <select> from overflowing the containing div in case it's shrunk */
max-width: 100%;
/* contents can overflow because of max-width limit, then show ellipsis */
@@ -824,6 +804,9 @@ table,
line-height: 1.5;
font-weight: 500;
}
+#crate-search:hover, #crate-search:focus {
+ border-color: var(--crate-search-hover-border);
+}
/* cancel stylistic differences in padding in firefox
for "appearance: none"-style (or equivalent) <select>s */
@-moz-document url-prefix() {
@@ -847,8 +830,13 @@ so that we can apply CSS-filters to change the arrow color in themes */
background-repeat: no-repeat;
background-size: 20px;
background-position: calc(100% - 2px) 56%;
- /* image is black color, themes should apply a "filter" property to change the color */
- background-image: /* AUTOREPLACE: */url("down-arrow.svg");
+ /* image is black color */
+ background-image: url("down-arrow-927217e04c7463ac.svg");
+ /* changes the arrow image color */
+ filter: var(--crate-search-div-filter);
+}
+#crate-search-div:hover::after, #crate-search-div:focus-within::after {
+ filter: var(--crate-search-div-hover-filter);
}
#crate-search > option {
font-size: 1rem;
@@ -873,40 +861,29 @@ so that we can apply CSS-filters to change the arrow color in themes */
.search-results {
display: none;
- padding-bottom: 2em;
}
.search-results.active {
display: block;
- /* prevent overhanging tabs from moving the first result */
- clear: both;
-}
-
-.search-results .desc > span {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- display: block;
}
.search-results > a {
- display: block;
+ display: flex;
/* A little margin ensures the browser's outlining of focused links has room to display. */
margin-left: 2px;
margin-right: 2px;
- border-bottom: 1px solid #aaa3;
+ border-bottom: 1px solid var(--search-result-border-color);
+ gap: 1em;
}
.search-results > a > div {
- display: flex;
- flex-flow: row wrap;
+ flex: 1;
}
-.search-results .result-name, .search-results div.desc {
- width: 50%;
-}
-.search-results .result-name {
- padding-right: 1em;
+.search-results > a > div.desc {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.search-results a:hover,
@@ -914,26 +891,32 @@ so that we can apply CSS-filters to change the arrow color in themes */
background-color: var(--search-result-link-focus-background-color);
}
+.search-results .result-name span.alias {
+ color: var(--search-results-alias-color);
+}
+.search-results .result-name span.grey {
+ color: var(--search-results-grey-color);
+}
+
.popover {
- font-size: 1rem;
position: absolute;
+ top: 100%;
right: 0;
z-index: 2;
display: block;
margin-top: 7px;
border-radius: 3px;
border: 1px solid var(--border-color);
- font-size: 1rem;
+ --popover-arrow-offset: 11px;
}
/* This rule is to draw the little arrow connecting the settings menu to the gear icon. */
.popover::before {
content: '';
position: absolute;
- right: 11px;
+ right: var(--popover-arrow-offset);
border: solid var(--border-color);
border-width: 1px 1px 0 0;
- display: inline-block;
padding: 4px;
transform: rotate(-45deg);
top: -5px;
@@ -947,16 +930,12 @@ so that we can apply CSS-filters to change the arrow color in themes */
/* use larger max-width for help popover, but not for help.html */
#help.popover {
max-width: 600px;
-}
-
-#help.popover::before {
- right: 48px;
+ --popover-arrow-offset: 48px;
}
#help dt {
float: left;
clear: left;
- display: block;
margin-right: 0.5rem;
}
#help span.top, #help span.bottom {
@@ -1024,11 +1003,9 @@ so that we can apply CSS-filters to change the arrow color in themes */
0 -1px 0 black;
}
-.module-item .stab,
-.import-item .stab {
+.item-left .stab {
border-radius: 3px;
display: inline-block;
- font-size: 0.875rem;
line-height: 1.2;
margin-bottom: 0;
margin-left: 0.3125em;
@@ -1048,7 +1025,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
.rightside {
padding-left: 12px;
- padding-right: 2px;
float: right;
}
@@ -1090,7 +1066,7 @@ pre.rust .bool-val {
pre.rust .self {
color: var(--code-highlight-self-color);
}
-pre.rust .attribute {
+pre.rust .attr {
color: var(--code-highlight-attribute-color);
}
pre.rust .macro,
@@ -1147,7 +1123,6 @@ pre.rust .doccomment {
.example-wrap .tooltip {
position: absolute;
display: block;
- cursor: pointer;
left: -25px;
top: 5px;
}
@@ -1164,6 +1139,8 @@ pre.rust .doccomment {
width: max-content;
top: -2px;
z-index: 1;
+ background-color: var(--tooltip-background-color);
+ color: var(--tooltip-color);
}
.example-wrap .tooltip::before {
@@ -1172,10 +1149,10 @@ pre.rust .doccomment {
top: 50%;
left: 16px;
margin-top: -5px;
- border-width: 5px;
- border-style: solid;
display: none;
z-index: 1;
+ border: 5px solid transparent;
+ border-right-color: var(--tooltip-background-color);
}
.example-wrap.ignore .tooltip::after {
@@ -1203,7 +1180,6 @@ pre.rust .doccomment {
}
a.test-arrow {
- display: inline-block;
visibility: hidden;
position: absolute;
padding: 5px 10px 5px 10px;
@@ -1212,6 +1188,12 @@ a.test-arrow {
top: 5px;
right: 5px;
z-index: 1;
+ color: var(--test-arrow-color);
+ background-color: var(--test-arrow-background-color);
+}
+a.test-arrow:hover {
+ color: var(--test-arrow-hover-color);
+ background-color: var(--test-arrow-hover-background-color);
}
.example-wrap:hover .test-arrow {
visibility: visible;
@@ -1231,12 +1213,6 @@ a.test-arrow {
font-size: 1.25rem;
}
-h3.variant {
- font-weight: 600;
- font-size: 1.125rem;
- margin-bottom: 10px;
-}
-
.sub-variant h4 {
font-size: 1rem;
font-weight: 400;
@@ -1253,59 +1229,40 @@ h3.variant {
margin-left: 24px;
}
-:target > code, :target > .code-header {
- opacity: 1;
-}
-
:target {
padding-right: 3px;
+ background-color: var(--target-background-color);
+ border-right: 3px solid var(--target-border-color);
}
-.notable-traits-tooltip {
- display: inline-block;
- cursor: pointer;
-}
-
-.notable-traits:hover .notable-traits-tooltiptext,
-.notable-traits .notable-traits-tooltiptext.force-tooltip {
- display: inline-block;
+.notable-traits {
+ color: inherit;
+ margin-right: 15px;
+ position: relative;
}
-.notable-traits .notable-traits-tooltiptext {
- display: none;
- padding: 5px 3px 3px 3px;
- border-radius: 6px;
- margin-left: 5px;
- z-index: 10;
- font-size: 1rem;
- cursor: default;
+/* placeholder thunk so that the mouse can easily travel from "(i)" to popover
+ the resulting "hover tunnel" is a stepped triangle, approximating
+ https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */
+.notable-traits:hover::after {
position: absolute;
- border: 1px solid;
-}
-
-.notable-traits-tooltip::after {
- /* The margin on the tooltip does not capture hover events,
- this extends the area of hover enough so that mouse hover is not
- lost when moving the mouse to the tooltip */
- content: "\00a0\00a0\00a0";
-}
-
-.notable-traits .docblock {
- margin: 0;
+ top: calc(100% - 10px);
+ left: -15px;
+ right: -15px;
+ height: 20px;
+ content: "\00a0";
}
-.notable-traits .notable {
- margin: 0;
- margin-bottom: 13px;
- font-size: 1.1875rem;
- font-weight: 600;
- display: block;
+.notable .docblock {
+ margin: 0.25em 0.5em;
}
-.notable-traits .docblock code.content {
+.notable .docblock pre, .notable .docblock code {
+ background: transparent;
margin: 0;
padding: 0;
font-size: 1.25rem;
+ white-space: pre-wrap;
}
.search-failed {
@@ -1335,7 +1292,6 @@ h3.variant {
#titles > button {
text-align: center;
font-size: 1.125rem;
- cursor: pointer;
border: 0;
border-top: 2px solid;
flex: 1;
@@ -1348,12 +1304,6 @@ h3.variant {
font-size: 1rem;
}
-.notable-traits {
- cursor: pointer;
- z-index: 2;
- margin-left: 5px;
-}
-
#sidebar-toggle {
position: sticky;
top: 0;
@@ -1362,8 +1312,8 @@ h3.variant {
border-bottom: 1px solid;
display: flex;
height: 40px;
- justify-content: center;
- align-items: center;
+ justify-content: stretch;
+ align-items: stretch;
z-index: 10;
}
#source-sidebar {
@@ -1376,44 +1326,51 @@ h3.variant {
border-bottom: 1px solid var(--border-color);
margin-bottom: 6px;
}
+#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
+#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
+ background-color: var(--source-sidebar-background-hover);
+}
+#source-sidebar div.files > a.selected {
+ background-color: var(--source-sidebar-background-selected);
+}
#sidebar-toggle > button {
font-size: inherit;
font-weight: bold;
background: none;
color: inherit;
- cursor: pointer;
text-align: center;
border: none;
outline: none;
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- /* work around button layout strangeness: https://stackoverflow.com/q/7271561 */
- width: 100%;
+ flex: 1 1;
/* iOS button gradient: https://stackoverflow.com/q/5438567 */
-webkit-appearance: none;
opacity: 1;
}
#settings-menu, #help-button {
margin-left: 4px;
- outline: none;
+ display: flex;
}
#settings-menu > a, #help-button > a, #copy-path {
width: 33px;
- cursor: pointer;
- line-height: 1.5;
}
#settings-menu > a, #help-button > a {
- padding: 5px;
- height: 100%;
- display: block;
+ display: flex;
+ align-items: center;
+ justify-content: center;
background-color: var(--button-background-color);
border: 1px solid var(--border-color);
border-radius: 2px;
+ color: var(--settings-button-color);
+ /* Rare exception to specifying font sizes in rem. Since this is acting
+ as an icon, it's okay to specify their sizes in pixels. */
+ font-size: 20px;
+}
+
+#settings-menu > a:hover, #settings-menu > a:focus,
+#help-button > a:hover, #help-button > a:focus {
+ border-color: var(--settings-button-border-focus);
}
#copy-path {
@@ -1444,14 +1401,6 @@ h3.variant {
animation: rotating 2s linear infinite;
}
-#help-button > a {
- text-align: center;
- /* Rare exception to specifying font sizes in rem. Since this is acting
- as an icon, it's okay to specify their sizes in pixels. */
- font-size: 20px;
- padding-top: 2px;
-}
-
kbd {
display: inline-block;
padding: 3px 5px;
@@ -1461,6 +1410,9 @@ kbd {
border: solid 1px var(--border-color);
border-radius: 3px;
cursor: default;
+ color: var(--kbd--color);
+ background-color: var(--kbd-background);
+ box-shadow: inset 0 -1px 0 var(--kbd-box-shadow-color);
}
ul.all-items > li {
@@ -1522,6 +1474,7 @@ details.rustdoc-toggle {
"Expand description" or "Show methods". */
details.rustdoc-toggle > summary.hideme {
cursor: pointer;
+ font-size: 1rem;
}
details.rustdoc-toggle > summary {
@@ -1539,15 +1492,15 @@ details.rustdoc-toggle > summary.hideme > span {
}
details.rustdoc-toggle > summary::before {
+ background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
content: "";
cursor: pointer;
width: 16px;
height: 16px;
- background-repeat: no-repeat;
- background-position: top left;
display: inline-block;
vertical-align: middle;
opacity: .5;
+ filter: var(--toggle-filter);
}
details.rustdoc-toggle > summary.hideme > span,
@@ -1584,13 +1537,6 @@ details.rustdoc-toggle > summary:focus-visible::before {
outline-offset: 1px;
}
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.top-doc > summary::before,
-details.rustdoc-toggle.non-exhaustive > summary,
-details.rustdoc-toggle.non-exhaustive > summary::before {
- font-size: 1rem;
-}
-
details.non-exhaustive {
margin-bottom: 8px;
}
@@ -1623,27 +1569,11 @@ details.rustdoc-toggle[open] > summary.hideme > span {
display: none;
}
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
- background-image: /* AUTOREPLACE: */url("toggle-minus.svg");
+details.rustdoc-toggle[open] > summary::before {
+ background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
}
-details.rustdoc-toggle > summary::before {
- background-image: /* AUTOREPLACE: */url("toggle-plus.svg");
-}
-
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
- width: 16px;
- height: 16px;
- background-repeat: no-repeat;
- background-position: top left;
- display: inline-block;
- content: "";
-}
-
-details.rustdoc-toggle[open] > summary::after,
-details.rustdoc-toggle[open] > summary.hideme::after {
+details.rustdoc-toggle[open] > summary::after {
content: "Collapse";
}
@@ -1677,7 +1607,6 @@ in storage.js
}
.rustdoc {
- padding-top: 0px;
/* Sidebar should overlay main content, rather than pushing main content to the right.
Turn off `display: flex` on the body element. */
display: block;
@@ -1702,10 +1631,6 @@ in storage.js
content: "Since ";
}
- #copy-path {
- display: none;
- }
-
/* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */
.sidebar .sidebar-logo,
@@ -1719,12 +1644,10 @@ in storage.js
/* Hide the sidebar offscreen while not in use. Doing this instead of display: none means
the sidebar stays visible for screen readers, which is useful for navigation. */
left: -1000px;
- margin-left: 0;
- margin: 0;
- padding: 0;
z-index: 11;
/* Reduce height slightly to account for mobile topbar. */
height: calc(100vh - 45px);
+ width: 200px;
}
/* The source view uses a different design for the sidebar toggle, and doesn't have a topbar,
@@ -1765,16 +1688,10 @@ in storage.js
white-space: nowrap;
}
- .mobile-topbar .logo-container {
- max-height: 45px;
- }
-
.mobile-topbar .logo-container > img {
max-width: 35px;
max-height: 35px;
- margin-left: 20px;
- margin-top: 5px;
- margin-bottom: 5px;
+ margin: 5px 0 5px 20px;
}
.mobile-topbar {
@@ -1800,7 +1717,6 @@ in storage.js
.sidebar-elems {
margin-top: 1em;
- background-color: var(--sidebar-background-color);
}
.content {
@@ -1815,21 +1731,6 @@ in storage.js
display: block;
}
- /* Because of ios, we need to actually have a full height sidebar title so the
- * actual sidebar can show up. But then we need to make it transparent so we don't
- * hide content. The filler just allows to create the background for the sidebar
- * title. But because of the absolute position, I had to lower the z-index.
- */
- #sidebar-filler {
- position: fixed;
- left: 45px;
- width: calc(100% - 45px);
- top: 0;
- height: 45px;
- z-index: -1;
- border-bottom: 1px solid;
- }
-
#main-content > details.rustdoc-toggle > summary::before,
#main-content > div > details.rustdoc-toggle > summary::before {
left: -11px;
@@ -1846,7 +1747,6 @@ in storage.js
z-index: 10;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
- cursor: pointer;
border: 1px solid;
border-left: 0;
}
@@ -1862,37 +1762,22 @@ in storage.js
border-bottom: 1px solid;
}
- .notable-traits .notable-traits-tooltiptext {
- left: 0;
- top: 100%;
- }
-
- /* We don't display the help button on mobile devices. */
- #help-button {
+ /* We don't display these buttons on mobile devices. */
+ #copy-path, #help-button {
display: none;
}
/* Display an alternating layout on tablets and phones */
- .item-table {
+ .item-table, .item-row, .item-left, .item-right,
+ .search-results > a, .search-results > a > div {
display: block;
}
- .item-row {
- display: flex;
- flex-flow: column wrap;
- }
- .item-left, .item-right {
- width: 100%;
- }
/* Display an alternating layout on tablets and phones */
.search-results > a {
- border-bottom: 1px solid #aaa9;
padding: 5px 0px;
}
- .search-results .result-name, .search-results div.desc {
- width: 100%;
- }
- .search-results div.desc, .item-right {
+ .search-results > a > div.desc, .item-right {
padding-left: 2em;
}
@@ -1922,6 +1807,22 @@ in storage.js
}
}
+/* Should have min-width: (N + 1)px where N is the mobile breakpoint above. */
+@media (min-width: 701px) {
+ /* Places file-link for a scraped example on top of the example to save space.
+ We only do this on large screens so the file-link doesn't overlap too much
+ with the example's content. */
+ .scraped-example-title {
+ position: absolute;
+ z-index: 10;
+ background: var(--main-background-color);
+ bottom: 8px;
+ right: 5px;
+ padding: 2px 4px;
+ box-shadow: 0 0 4px var(--main-background-color);
+ }
+}
+
@media print {
nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path,
details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before,
@@ -1969,24 +1870,28 @@ in storage.js
}
}
-.method-toggle > summary,
+.variant,
.implementors-toggle > summary,
.impl,
#implementors-list > .docblock,
.impl-items > section,
-.methods > section
+.impl-items > .rustdoc-toggle > summary,
+.methods > section,
+.methods > .rustdoc-toggle > summary
{
margin-bottom: 0.75em;
}
-.method-toggle[open]:not(:last-child),
+.variants > .docblock,
+.impl-items > .rustdoc-toggle[open]:not(:last-child),
+.methods > .rustdoc-toggle[open]:not(:last-child),
.implementors-toggle[open]:not(:last-child) {
margin-bottom: 2em;
}
-#trait-implementations-list .method-toggle:not(:last-child),
-#synthetic-implementations-list .method-toggle:not(:last-child),
-#blanket-implementations-list .method-toggle:not(:last-child) {
+#trait-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
+#synthetic-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
+#blanket-implementations-list .impl-items > .rustdoc-toggle:not(:last-child) {
margin-bottom: 1em;
}
@@ -1999,12 +1904,16 @@ in storage.js
font-size: 12px;
position: relative;
bottom: 1px;
- background: transparent;
border-width: 1px;
border-style: solid;
border-radius: 50px;
}
+.scraped-example {
+ /* So .scraped-example-title can be positioned absolutely */
+ position: relative;
+}
+
.scraped-example .code-wrapper {
position: relative;
display: flex;
@@ -2014,54 +1923,62 @@ in storage.js
}
.scraped-example:not(.expanded) .code-wrapper {
- max-height: 240px;
+ /* scrape-examples.js has a constant DEFAULT_MAX_LINES (call it N) for the number
+ * of lines shown in the un-expanded example code viewer. This pre needs to have
+ * a max-height equal to line-height * N. The line-height is currently 1.5em,
+ * and we include additional 10px for padding. */
+ max-height: calc(1.5em * 5 + 10px);
}
.scraped-example:not(.expanded) .code-wrapper pre {
overflow-y: hidden;
- max-height: 240px;
padding-bottom: 0;
+ /* See above comment, should be the same max-height. */
+ max-height: calc(1.5em * 5 + 10px);
}
-.scraped-example .code-wrapper .prev {
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre {
+ /* See above comment, except this height is based on HIDDEN_MAX_LINES. */
+ max-height: calc(1.5em * 10 + 10px);
+}
+
+.scraped-example .code-wrapper .next,
+.scraped-example .code-wrapper .prev,
+.scraped-example .code-wrapper .expand {
+ color: var(--main-color);
position: absolute;
top: 0.25em;
+ z-index: 1;
+ padding: 0;
+ background: none;
+ border: none;
+ /* iOS button gradient: https://stackoverflow.com/q/5438567 */
+ -webkit-appearance: none;
+ opacity: 1;
+}
+.scraped-example .code-wrapper .prev {
right: 2.25em;
- z-index: 100;
- cursor: pointer;
}
-
.scraped-example .code-wrapper .next {
- position: absolute;
- top: 0.25em;
right: 1.25em;
- z-index: 100;
- cursor: pointer;
}
-
.scraped-example .code-wrapper .expand {
- position: absolute;
- top: 0.25em;
right: 0.25em;
- z-index: 100;
- cursor: pointer;
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper:before,
+.scraped-example:not(.expanded) .code-wrapper:after {
content: " ";
width: 100%;
height: 5px;
position: absolute;
- z-index: 100;
+ z-index: 1;
+}
+.scraped-example:not(.expanded) .code-wrapper:before {
top: 0;
}
-
.scraped-example:not(.expanded) .code-wrapper:after {
- content: " ";
- width: 100%;
- height: 5px;
- position: absolute;
- z-index: 100;
bottom: 0;
}
@@ -2070,12 +1987,15 @@ in storage.js
padding: 14px 0;
}
+.scraped-example .code-wrapper .src-line-numbers a,
.scraped-example .code-wrapper .src-line-numbers span {
padding: 0 14px;
}
.scraped-example .code-wrapper .example-wrap {
- flex: 1;
+ display: grid;
+ grid-template-columns: max-content auto;
+ width: 100%;
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 0;
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
index 83939f63b..1f6fb961e 100644
--- a/src/librustdoc/html/static/css/settings.css
+++ b/src/librustdoc/html/static/css/settings.css
@@ -8,7 +8,8 @@
flex-wrap: wrap;
}
-.setting-line .radio-line input {
+.setting-line .radio-line input,
+.setting-line .toggle input {
margin-right: 0.3em;
height: 1.2rem;
width: 1.2rem;
@@ -17,9 +18,18 @@
outline: none;
-webkit-appearance: none;
cursor: pointer;
+}
+.setting-line .radio-line input {
border-radius: 50%;
}
-.setting-line .radio-line input + span {
+.setting-line .toggle input:checked {
+ 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-line .radio-line input + span,
+.setting-line .toggle span {
padding-bottom: 1px;
}
@@ -49,37 +59,6 @@
cursor: pointer;
}
-.toggle input {
- opacity: 0;
- position: absolute;
-}
-
-.slider {
- position: relative;
- width: 45px;
- min-width: 45px;
- display: block;
- height: 28px;
- margin-right: 20px;
- cursor: pointer;
- background-color: #ccc;
- transition: .3s;
-}
-
-.slider:before {
- position: absolute;
- content: "";
- height: 19px;
- width: 19px;
- left: 4px;
- bottom: 4px;
- transition: .3s;
-}
-
-input:checked + .slider:before {
- transform: translateX(19px);
-}
-
.setting-line > .sub-settings {
padding-left: 42px;
width: 100%;
@@ -94,7 +73,11 @@ input:checked + .slider:before {
box-shadow: inset 0 0 0 3px var(--main-background-color);
background-color: var(--settings-input-color);
}
-.setting-line .radio-line input:focus {
+.setting-line .toggle input:checked {
+ background-color: var(--settings-input-color);
+}
+.setting-line .radio-line input:focus,
+.setting-line .toggle input:focus {
box-shadow: 0 0 1px 1px var(--settings-input-color);
}
/* In here we combine both `:focus` and `:checked` properties. */
@@ -102,9 +85,7 @@ input:checked + .slider:before {
box-shadow: inset 0 0 0 3px var(--main-background-color),
0 0 2px 2px var(--settings-input-color);
}
-.setting-line .radio-line input:hover {
+.setting-line .radio-line input:hover,
+.setting-line .toggle input:hover {
border-color: var(--settings-input-color) !important;
}
-input:checked + .slider {
- background-color: var(--settings-input-color);
-}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index fdfdb3e19..0436fe013 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -7,6 +7,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--main-background-color: #0f1419;
--main-color: #c5c5c5;
--settings-input-color: #ffb454;
+ --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;
@@ -19,6 +21,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--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%);
@@ -38,9 +41,12 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--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-results-alias-color: #c5c5c5;
+ --search-results-grey-color: #999;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
- --search-color: #fff;
--code-highlight-kw-color: #ff7733;
--code-highlight-kw-2-color: #ff7733;
--code-highlight-lifetime-color: #ff7733;
@@ -58,16 +64,30 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--example-line-numbers-border-color: none;
--src-line-numbers-span-color: #5c6773;
--src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
+ --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);
+ --tooltip-background-color: #314559;
+ --tooltip-color: #c5c5c5;
+ --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;
+ --source-sidebar-background-selected: #14191f;
+ --source-sidebar-background-hover: #14191f;
+ --table-alt-row-background-color: #191f26;
}
h1, h2, h3, h4 {
@@ -99,13 +119,6 @@ pre, .rustdoc.source .example-wrap {
color: #e6e1cf;
}
-.rust-logo {
- filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
-}
-
.sidebar .current,
.sidebar a:hover {
color: #ffb44c;
@@ -147,21 +160,6 @@ body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #e0e0e0 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);
-}
-
.module-item .stab,
.import-item .stab {
color: #000;
@@ -171,40 +169,6 @@ details.rustdoc-toggle > summary::before {
color: #788797;
}
-a.test-arrow {
- font-size: 100%;
- color: #788797;
- border-radius: 4px;
- background-color: rgba(57, 175, 215, 0.09);
-}
-
-a.test-arrow:hover {
- background-color: rgba(57, 175, 215, 0.368);
- color: #c5c5c5;
-}
-
-:target {
- background: rgba(255, 236, 164, 0.06);
- border-right: 3px solid rgba(255, 180, 76, 0.85);
-}
-
-.search-failed a {
- color: #39AFD7;
-}
-
-.tooltip::after {
- background-color: #314559;
- color: #c5c5c5;
-}
-
-.tooltip::before {
- border-color: transparent #314559 transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #314559;
-}
-
#titles > button.selected {
background-color: #141920 !important;
border-bottom: 1px solid #ffb44c !important;
@@ -236,42 +200,16 @@ pre.rust .kw {}
pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, pre.rust .attribute {}
pre.rust .kw-2, pre.rust .prelude-ty {}
-kbd {
- color: #c5c5c5;
- background-color: #314559;
- box-shadow: inset 0 -1px 0 #5c6773;
-}
-
-#settings-menu > a, #help-button > a {
- color: #fff;
-}
-
#settings-menu > a img {
filter: invert(100);
}
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #e0e0e0;
-}
-
-.search-results .result-name span.alias {
- color: #c5c5c5;
-}
-.search-results .result-name span.grey {
- color: #999;
-}
-
#source-sidebar > .title {
color: #fff;
}
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #14191f;
- color: #ffb44c;
-}
+#source-sidebar div.files > a:focus, details.dir-entry summary:focus,
#source-sidebar div.files > a.selected {
- background-color: #14191f;
color: #ffb44c;
}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 361d3d4a2..d945e956c 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -2,6 +2,8 @@
--main-background-color: #353535;
--main-color: #ddd;
--settings-input-color: #2196f3;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
--sidebar-background-color: #505050;
--sidebar-background-color-hover: #676767;
--code-block-background-color: #2A2A2A;
@@ -14,6 +16,7 @@
--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%);
@@ -33,9 +36,12 @@
--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-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
- --search-color: #111;
--code-highlight-kw-color: #ab8ac1;
--code-highlight-kw-2-color: #769acb;
--code-highlight-lifetime-color: #d97f26;
@@ -53,23 +59,30 @@
--example-line-numbers-border-color: #4a4949;
--src-line-numbers-span-color: #3b91e2;
--src-line-number-highlighted-background-color: #0a042f;
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
-}
-
-.rust-logo {
- filter: drop-shadow(1px 0 0px #fff)
+ --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;
+ --tooltip-background-color: #000;
+ --tooltip-color: #fff;
+ --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)
+ 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;
+ --source-sidebar-background-selected: #333;
+ --source-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2A2A2A;
}
.content .item-info::before { color: #ccc; }
@@ -78,53 +91,6 @@ body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #2196f3 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);
-}
-
-a.test-arrow {
- color: #dedede;
- background-color: rgba(78, 139, 202, 0.2);
-}
-
-a.test-arrow:hover{
- background-color: #4e8bca;
-}
-
-:target {
- background-color: #494a3d;
- border-right: 3px solid #bb7410;
-}
-
-.search-failed a {
- color: #0089ff;
-}
-
-.tooltip::after {
- background-color: #000;
- color: #fff;
- border-color: #000;
-}
-
-.tooltip::before {
- border-color: transparent black transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #111;
-}
-
#titles > button:not(.selected) {
background-color: #252525;
border-top-color: #252525;
@@ -139,36 +105,6 @@ a.test-arrow:hover{
color: #888;
}
-kbd {
- color: #000;
- background-color: #fafbfc;
- box-shadow: inset 0 -1px 0 #c6cbd1;
-}
-
-#settings-menu > a, #help-button > a {
- color: #000;
-}
-
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #ffb900;
-}
-
-.search-results .result-name span.alias {
- color: #fff;
-}
-.search-results .result-name span.grey {
- color: #ccc;
-}
-
-#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #444;
-}
-#source-sidebar div.files > a.selected {
- background-color: #333;
-}
-
.scraped-example-list .scrape-help {
border-color: #aaa;
color: #eee;
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index 5eb4bbcf8..58955a793 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -2,6 +2,8 @@
--main-background-color: white;
--main-color: black;
--settings-input-color: #2196f3;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
--sidebar-background-color: #F5F5F5;
--sidebar-background-color-hover: #E0E0E0;
--code-block-background-color: #F5F5F5;
@@ -14,6 +16,7 @@
--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%);
@@ -33,9 +36,12 @@
--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-results-alias-color: #000;
+ --search-results-grey-color: #999;
--stab-background-color: #fff5d6;
--stab-code-color: #000;
- --search-color: #000;
--code-highlight-kw-color: #8959a8;
--code-highlight-kw-2-color: #4271ae;
--code-highlight-lifetime-color: #b76514;
@@ -53,22 +59,27 @@
--example-line-numbers-border-color: #c7c7c7;
--src-line-numbers-span-color: #c67e2d;
--src-line-number-highlighted-background-color: #fdffd3;
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
-}
-
-.rust-logo {
- /* This rule exists to force other themes to explicitly style the logo.
- * Rustdoc has a custom linter for this purpose.
- */
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --tooltip-background-color: #000;
+ --tooltip-color: #fff;
+ --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;
+ --source-sidebar-background-selected: #fff;
+ --source-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #F5F5F5;
}
.content .item-info::before { color: #ccc; }
@@ -77,48 +88,6 @@ body.source .example-wrap pre.rust a {
background: #eee;
}
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #717171 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);
-}
-
-a.test-arrow {
- color: #f5f5f5;
- background-color: rgba(78, 139, 202, 0.2);
-}
-
-a.test-arrow:hover{
- background-color: #4e8bca;
-}
-
-:target {
- background: #FDFFD3;
- border-right: 3px solid #AD7C37;
-}
-
-.search-failed a {
- color: #3873AD;
-}
-
-.tooltip::after {
- background-color: #000;
- color: #fff;
-}
-
-.tooltip::before {
- border-color: transparent black transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #eee;
-}
-
#titles > button:not(.selected) {
background-color: #e6e6e6;
border-top-color: #e6e6e6;
@@ -133,35 +102,6 @@ a.test-arrow:hover{
color: #888;
}
-kbd {
- color: #000;
- background-color: #fafbfc;
- box-shadow: inset 0 -1px 0 #c6cbd1;
-}
-
-#settings-menu > a, #help-button > a {
- color: #000;
-}
-
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #717171;
-}
-
-.search-results .result-name span.alias {
- color: #000;
-}
-.search-results .result-name span.grey {
- color: #999;
-}
-
-#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #E0E0E0;
-}
-#source-sidebar div.files > a.selected {
- background-color: #fff;
-}
.scraped-example-list .scrape-help {
border-color: #555;
color: #333;
diff --git a/src/librustdoc/html/static/fonts/README.txt b/src/librustdoc/html/static/fonts/README.txt
new file mode 100644
index 000000000..0db15996d
--- /dev/null
+++ b/src/librustdoc/html/static/fonts/README.txt
@@ -0,0 +1,12 @@
+The Nanum Barun Gothic fonts are shipped with rustdoc because the default fonts
+on many Windows installs render Korean very badly. See:
+ - https://github.com/rust-lang/rust/pull/84048,
+ - https://github.com/rust-lang/rust/issues/84035
+ - https://github.com/rust-lang/rust/pull/90232
+
+The font files were generated with these commands:
+
+```sh
+pyftsubset NanumBarunGothic.ttf \
+--unicodes=U+AC00-D7AF:U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
+--output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 33480fa41..152116089 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -47,10 +47,8 @@ function blurHandler(event, parentElem, hideCallback) {
}
}
-(function() {
- window.rootPath = getVar("root-path");
- window.currentCrate = getVar("current-crate");
-}());
+window.rootPath = getVar("root-path");
+window.currentCrate = getVar("current-crate");
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
@@ -183,9 +181,9 @@ function browserSupportsHistoryApi() {
}
// eslint-disable-next-line no-unused-vars
-function loadCss(cssFileName) {
+function loadCss(cssUrl) {
const link = document.createElement("link");
- link.href = resourcePath(cssFileName, ".css");
+ link.href = cssUrl;
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
@@ -204,12 +202,13 @@ function loadCss(cssFileName) {
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
+ window.hideAllModals(false);
addClass(getSettingsButton(), "rotate");
event.preventDefault();
// Sending request for the CSS and the JS files at the same time so it will
// hopefully be loaded when the JS will generate the settings content.
- loadCss("settings");
- loadScript(resourcePath("settings", ".js"));
+ loadCss(getVar("static-root-path") + getVar("settings-css"));
+ loadScript(getVar("static-root-path") + getVar("settings-js"));
};
window.searchState = {
@@ -286,7 +285,7 @@ function loadCss(cssFileName) {
function loadSearch() {
if (!searchLoaded) {
searchLoaded = true;
- loadScript(resourcePath("search", ".js"));
+ loadScript(getVar("static-root-path") + getVar("search-js"));
loadScript(resourcePath("search-index", ".js"));
}
}
@@ -303,13 +302,15 @@ function loadCss(cssFileName) {
const params = searchState.getQueryStringParams();
if (params.search !== undefined) {
- const search = searchState.outputElement();
- search.innerHTML = "<h3 class=\"search-loading\">" +
- searchState.loadingText + "</h3>";
- searchState.showResults(search);
+ searchState.setLoadingSearch();
loadSearch();
}
},
+ setLoadingSearch: () => {
+ const search = searchState.outputElement();
+ search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>";
+ searchState.showResults(search);
+ },
};
function getPageId() {
@@ -379,7 +380,7 @@ function loadCss(cssFileName) {
}
ev.preventDefault();
searchState.defocus();
- window.hidePopoverMenus();
+ window.hideAllModals(true); // true = reset focus for notable traits
}
function handleShortcut(ev) {
@@ -621,7 +622,7 @@ function loadCss(cssFileName) {
const innerToggle = document.getElementById(toggleAllDocsId);
removeClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
- if (!hasClass(e, "type-contents-toggle")) {
+ if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
e.open = true;
}
});
@@ -725,12 +726,9 @@ function loadCss(cssFileName) {
});
};
- (function() {
- // To avoid checking on "rustdoc-line-numbers" value on every loop...
- if (getSettingValue("line-numbers") === "true") {
- window.rustdoc_add_line_numbers_to_examples();
- }
- }());
+ if (getSettingValue("line-numbers") === "true") {
+ window.rustdoc_add_line_numbers_to_examples();
+ }
let oldSidebarScrollPosition = null;
@@ -772,6 +770,7 @@ function loadCss(cssFileName) {
};
function showSidebar() {
+ window.hideAllModals(false);
window.rustdocMobileScrollLock();
const sidebar = document.getElementsByClassName("sidebar")[0];
addClass(sidebar, "shown");
@@ -790,6 +789,19 @@ function loadCss(cssFileName) {
// we need to switch away from mobile mode and make the main content area scrollable.
hideSidebar();
}
+ if (window.CURRENT_NOTABLE_ELEMENT) {
+ // As a workaround to the behavior of `contains: layout` used in doc togglers, the
+ // notable traits popup is positioned using javascript.
+ //
+ // This means when the window is resized, we need to redo the layout.
+ const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE;
+ const force_visible = base.NOTABLE_FORCE_VISIBLE;
+ hideNotable(false);
+ if (force_visible) {
+ showNotable(base);
+ base.NOTABLE_FORCE_VISIBLE = true;
+ }
+ }
});
function handleClick(id, f) {
@@ -822,10 +834,123 @@ function loadCss(cssFileName) {
});
});
+ function showNotable(e) {
+ if (!window.NOTABLE_TRAITS) {
+ const data = document.getElementById("notable-traits-data");
+ if (data) {
+ window.NOTABLE_TRAITS = JSON.parse(data.innerText);
+ } else {
+ throw new Error("showNotable() called on page without any notable traits!");
+ }
+ }
+ if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) {
+ // Make this function idempotent.
+ return;
+ }
+ window.hideAllModals(false);
+ const ty = e.getAttribute("data-ty");
+ const wrapper = document.createElement("div");
+ wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
+ wrapper.className = "notable popover";
+ const focusCatcher = document.createElement("div");
+ focusCatcher.setAttribute("tabindex", "0");
+ focusCatcher.onfocus = hideNotable;
+ wrapper.appendChild(focusCatcher);
+ const pos = e.getBoundingClientRect();
+ // 5px overlap so that the mouse can easily travel from place to place
+ wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px";
+ wrapper.style.left = 0;
+ wrapper.style.right = "auto";
+ wrapper.style.visibility = "hidden";
+ const body = document.getElementsByTagName("body")[0];
+ body.appendChild(wrapper);
+ const wrapperPos = wrapper.getBoundingClientRect();
+ // offset so that the arrow points at the center of the "(i)"
+ const finalPos = pos.left + window.scrollX - wrapperPos.width + 24;
+ if (finalPos > 0) {
+ wrapper.style.left = finalPos + "px";
+ } else {
+ wrapper.style.setProperty(
+ "--popover-arrow-offset",
+ (wrapperPos.right - pos.right + 4) + "px"
+ );
+ }
+ wrapper.style.visibility = "";
+ window.CURRENT_NOTABLE_ELEMENT = wrapper;
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e;
+ wrapper.onpointerleave = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
+ hideNotable(true);
+ }
+ };
+ }
+
+ function notableBlurHandler(event) {
+ if (window.CURRENT_NOTABLE_ELEMENT &&
+ !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT) &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT) &&
+ !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE)
+ ) {
+ // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari.
+ // When I click the button on an already-opened notable trait popover, Safari
+ // hides the popover and then immediately shows it again, while everyone else hides it
+ // and it stays hidden.
+ //
+ // To work around this, make sure the click finishes being dispatched before
+ // hiding the popover. Since `hideNotable()` is idempotent, this makes Safari behave
+ // consistently with the other two.
+ setTimeout(() => hideNotable(false), 0);
+ }
+ }
+
+ function hideNotable(focus) {
+ if (window.CURRENT_NOTABLE_ELEMENT) {
+ if (window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE) {
+ if (focus) {
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.focus();
+ }
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false;
+ }
+ const body = document.getElementsByTagName("body")[0];
+ body.removeChild(window.CURRENT_NOTABLE_ELEMENT);
+ window.CURRENT_NOTABLE_ELEMENT = null;
+ }
+ }
+
onEachLazy(document.getElementsByClassName("notable-traits"), e => {
e.onclick = function() {
- this.getElementsByClassName("notable-traits-tooltiptext")[0]
- .classList.toggle("force-tooltip");
+ this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true;
+ if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) {
+ hideNotable(true);
+ } else {
+ showNotable(this);
+ window.CURRENT_NOTABLE_ELEMENT.setAttribute("tabindex", "0");
+ window.CURRENT_NOTABLE_ELEMENT.focus();
+ window.CURRENT_NOTABLE_ELEMENT.onblur = notableBlurHandler;
+ }
+ return false;
+ };
+ e.onpointerenter = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ showNotable(this);
+ };
+ e.onpointerleave = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ if (!this.NOTABLE_FORCE_VISIBLE &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
+ hideNotable(true);
+ }
};
});
@@ -929,6 +1054,17 @@ function loadCss(cssFileName) {
}
/**
+ * Hide popover menus, notable trait tooltips, and the sidebar (if applicable).
+ *
+ * Pass "true" to reset focus for notable traits.
+ */
+ window.hideAllModals = function(switchFocus) {
+ hideSidebar();
+ window.hidePopoverMenus();
+ hideNotable(switchFocus);
+ };
+
+ /**
* Hide all the popover menus.
*/
window.hidePopoverMenus = function() {
@@ -959,7 +1095,7 @@ function loadCss(cssFileName) {
function showHelp() {
const menu = getHelpMenu(true);
if (menu.style.display === "none") {
- window.hidePopoverMenus();
+ window.hideAllModals();
menu.style.display = "";
}
}
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index d0fd115fd..7a3a9c5f3 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -3,25 +3,33 @@
"use strict";
(function() {
- // Number of lines shown when code viewer is not expanded
- const MAX_LINES = 10;
+ // Number of lines shown when code viewer is not expanded.
+ // DEFAULT is the first example shown by default, while HIDDEN is
+ // the examples hidden beneath the "More examples" toggle.
+ //
+ // NOTE: these values MUST be synchronized with certain rules in rustdoc.css!
+ const DEFAULT_MAX_LINES = 5;
+ const HIDDEN_MAX_LINES = 10;
// Scroll code block to the given code location
- function scrollToLoc(elt, loc) {
+ function scrollToLoc(elt, loc, isHidden) {
const lines = elt.querySelector(".src-line-numbers");
let scrollOffset;
// If the block is greater than the size of the viewer,
// then scroll to the top of the block. Otherwise scroll
// to the middle of the block.
- if (loc[1] - loc[0] > MAX_LINES) {
+ const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
+ if (loc[1] - loc[0] > maxLines) {
const line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
const wrapper = elt.querySelector(".code-wrapper");
const halfHeight = wrapper.offsetHeight / 2;
- const offsetMid = (lines.children[loc[0]].offsetTop
- + lines.children[loc[1]].offsetTop) / 2;
+ const offsetTop = lines.children[loc[0]].offsetTop;
+ const lastLine = lines.children[loc[1]];
+ const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
+ const offsetMid = (offsetTop + offsetBot) / 2;
scrollOffset = offsetMid - halfHeight;
}
@@ -29,7 +37,7 @@
elt.querySelector(".rust").scrollTo(0, scrollOffset);
}
- function updateScrapedExample(example) {
+ function updateScrapedExample(example, isHidden) {
const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
let locIndex = 0;
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
@@ -40,7 +48,7 @@
const onChangeLoc = changeIndex => {
removeClass(highlights[locIndex], "focus");
changeIndex();
- scrollToLoc(example, locs[locIndex][0]);
+ scrollToLoc(example, locs[locIndex][0], isHidden);
addClass(highlights[locIndex], "focus");
const url = locs[locIndex][1];
@@ -57,7 +65,7 @@
});
});
- example.querySelector("next")
+ example.querySelector(".next")
.addEventListener("click", () => {
onChangeLoc(() => {
locIndex = (locIndex + 1) % locs.length;
@@ -70,7 +78,7 @@
expandButton.addEventListener("click", () => {
if (hasClass(example, "expanded")) {
removeClass(example, "expanded");
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
} else {
addClass(example, "expanded");
}
@@ -78,11 +86,11 @@
}
// Start with the first example in view
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
}
const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");
- onEachLazy(firstExamples, updateScrapedExample);
+ onEachLazy(firstExamples, el => updateScrapedExample(el, false));
onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => {
// Allow users to click the left border of the <details> section to close it,
// since the section can be large and finding the [+] button is annoying.
@@ -99,7 +107,7 @@
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
setTimeout(() => {
- onEachLazy(moreExamples, updateScrapedExample);
+ onEachLazy(moreExamples, el => updateScrapedExample(el, true));
});
}, {once: true});
});
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index d04ec357c..23ae4e970 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1491,6 +1491,7 @@ function initSearch(rawSearchIndex) {
const target = searchState.focusedByTab[searchState.currentTab] ||
document.querySelectorAll(".search-results.active a").item(0) ||
document.querySelectorAll("#titles > button").item(searchState.currentTab);
+ searchState.focusedByTab[searchState.currentTab] = null;
if (target) {
target.focus();
}
@@ -1593,7 +1594,6 @@ function initSearch(rawSearchIndex) {
link.className = "result-" + type;
link.href = item.href;
- const wrapper = document.createElement("div");
const resultName = document.createElement("div");
resultName.className = "result-name";
@@ -1614,16 +1614,13 @@ function initSearch(rawSearchIndex) {
resultName.insertAdjacentHTML(
"beforeend",
item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>");
- wrapper.appendChild(resultName);
+ link.appendChild(resultName);
const description = document.createElement("div");
description.className = "desc";
- const spanDesc = document.createElement("span");
- spanDesc.insertAdjacentHTML("beforeend", item.desc);
+ description.insertAdjacentHTML("beforeend", item.desc);
- description.appendChild(spanDesc);
- wrapper.appendChild(description);
- link.appendChild(wrapper);
+ link.appendChild(description);
output.appendChild(link);
});
} else if (query.error === null) {
@@ -1769,13 +1766,13 @@ function initSearch(rawSearchIndex) {
* @param {boolean} [forced]
*/
function search(e, forced) {
- const params = searchState.getQueryStringParams();
- const query = parseQuery(searchState.input.value.trim());
-
if (e) {
e.preventDefault();
}
+ const query = parseQuery(searchState.input.value.trim());
+ let filterCrates = getFilterCrates();
+
if (!forced && query.userQuery === currentResults) {
if (query.userQuery.length > 0) {
putBackSearch();
@@ -1783,7 +1780,9 @@ function initSearch(rawSearchIndex) {
return;
}
- let filterCrates = getFilterCrates();
+ searchState.setLoadingSearch();
+
+ const params = searchState.getQueryStringParams();
// In case we have no information about the saved crate and there is a URL query parameter,
// we override it with the URL query parameter.
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 5e1c7e6f0..589bfc793 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -9,13 +9,16 @@
const isSettingsPage = window.location.pathname.endsWith("/settings.html");
function changeSetting(settingName, value) {
+ if (settingName === "theme") {
+ const useSystem = value === "system preference" ? "true" : "false";
+ updateLocalStorage("use-system-theme", useSystem);
+ }
updateLocalStorage(settingName, value);
switch (settingName) {
case "theme":
case "preferred-dark-theme":
case "preferred-light-theme":
- case "use-system-theme":
updateSystemTheme();
updateLightAndDark();
break;
@@ -45,7 +48,6 @@
}
function showLightAndDark() {
- addClass(document.getElementById("theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
}
@@ -53,11 +55,11 @@
function hideLightAndDark() {
addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
- removeClass(document.getElementById("theme").parentElement, "hidden");
}
function updateLightAndDark() {
- if (getSettingValue("use-system-theme") !== "false") {
+ const useSystem = getSettingValue("use-system-theme");
+ if (useSystem === "true" || (useSystem === null && getSettingValue("theme") === null)) {
showLightAndDark();
} else {
hideLightAndDark();
@@ -66,8 +68,7 @@
function setEvents(settingsElement) {
updateLightAndDark();
- onEachLazy(settingsElement.getElementsByClassName("slider"), elem => {
- const toggle = elem.previousElementSibling;
+ onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => {
const settingId = toggle.id;
const settingValue = getSettingValue(settingId);
if (settingValue !== null) {
@@ -92,7 +93,18 @@
});
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
const settingId = elem.name;
- const settingValue = getSettingValue(settingId);
+ let settingValue = getSettingValue(settingId);
+ if (settingId === "theme") {
+ const useSystem = getSettingValue("use-system-theme");
+ if (useSystem === "true" || settingValue === null) {
+ if (useSystem !== "false") {
+ settingValue = "system preference";
+ } else {
+ // This is the default theme.
+ settingValue = "light";
+ }
+ }
+ }
if (settingValue !== null && settingValue !== "null") {
elem.checked = settingValue === elem.value;
}
@@ -121,27 +133,30 @@
if (setting["options"] !== undefined) {
// This is a select setting.
- output += `<div class="radio-line" id="${js_data_name}">\
- <span class="setting-name">${setting_name}</span>\
- <div class="choices">`;
+ output += `\
+<div class="radio-line" id="${js_data_name}">
+ <span class="setting-name">${setting_name}</span>
+<div class="choices">`;
onEach(setting["options"], option => {
const checked = option === setting["default"] ? " checked" : "";
+ const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
- output += `<label for="${js_data_name}-${option}" class="choice">\
- <input type="radio" name="${js_data_name}" \
- id="${js_data_name}-${option}" value="${option}"${checked}>\
- <span>${option}</span>\
- </label>`;
+ output += `\
+<label for="${full}" class="choice">
+ <input type="radio" name="${js_data_name}"
+ id="${full}" value="${option}"${checked}>
+ <span>${option}</span>
+</label>`;
});
output += "</div></div>";
} else {
// This is a toggle.
const checked = setting["default"] === true ? " checked" : "";
- output += `<label class="toggle">\
- <input type="checkbox" id="${js_data_name}"${checked}>\
- <span class="slider"></span>\
- <span class="label">${setting_name}</span>\
- </label>`;
+ output += `\
+<label class="toggle">\
+ <input type="checkbox" id="${js_data_name}"${checked}>\
+ <span class="label">${setting_name}</span>\
+</label>`;
}
output += "</div>";
}
@@ -154,30 +169,27 @@
* @return {HTMLElement}
*/
function buildSettingsPage() {
- const themes = getVar("themes").split(",");
+ const theme_names = getVar("themes").split(",").filter(t => t);
+ theme_names.push("light", "dark", "ayu");
+
const settings = [
{
- "name": "Use system theme",
- "js_name": "use-system-theme",
- "default": true,
- },
- {
"name": "Theme",
"js_name": "theme",
- "default": "light",
- "options": themes,
+ "default": "system preference",
+ "options": theme_names.concat("system preference"),
},
{
"name": "Preferred light theme",
"js_name": "preferred-light-theme",
"default": "light",
- "options": themes,
+ "options": theme_names,
},
{
"name": "Preferred dark theme",
"js_name": "preferred-dark-theme",
"default": "dark",
- "options": themes,
+ "options": theme_names,
},
{
"name": "Auto-hide item contents for large items",
@@ -256,7 +268,7 @@
event.preventDefault();
const shouldDisplaySettings = settingsMenu.style.display === "none";
- window.hidePopoverMenus();
+ window.hideAllModals();
if (shouldDisplaySettings) {
displaySettings();
}
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index 0b9368dd8..5db768c1c 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -157,7 +157,7 @@ function highlightSourceLines(match) {
x.scrollIntoView();
}
onEachLazy(document.getElementsByClassName("src-line-numbers"), e => {
- onEachLazy(e.getElementsByTagName("span"), i_e => {
+ onEachLazy(e.getElementsByTagName("a"), i_e => {
removeClass(i_e, "line-highlighted");
});
});
@@ -188,8 +188,13 @@ const handleSourceHighlight = (function() {
return ev => {
let cur_line_id = parseInt(ev.target.id, 10);
- // It can happen when clicking not on a line number span.
- if (isNaN(cur_line_id)) {
+ // This event handler is attached to the entire line number column, but it should only
+ // be run if one of the anchors is clicked. It also shouldn't do anything if the anchor
+ // is clicked with a modifier key (to open a new browser tab).
+ if (isNaN(cur_line_id) ||
+ ev.ctrlKey ||
+ ev.altKey ||
+ ev.metaKey) {
return;
}
ev.preventDefault();
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index b462a2c50..db2db83ca 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -126,33 +126,29 @@ function getCurrentValue(name) {
}
}
-function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
- const newHref = mainStyleElem.href.replace(
- /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
-
+function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
// If this new value comes from a system setting or from the previously
// saved theme, no need to save it.
if (saveTheme) {
- updateLocalStorage("theme", newTheme);
- }
-
- if (styleElem.href === newHref) {
- return;
+ updateLocalStorage("theme", newThemeName);
}
- let found = false;
if (savedHref.length === 0) {
onEachLazy(document.getElementsByTagName("link"), el => {
savedHref.push(el.href);
});
}
- onEach(savedHref, el => {
- if (el === newHref) {
- found = true;
+ const newHref = savedHref.find(url => {
+ const m = url.match(/static\.files\/(.*)-[a-f0-9]{16}\.css$/);
+ if (m && m[1] === newThemeName) {
+ return true;
+ }
+ const m2 = url.match(/\/([^/]*)\.css$/);
+ if (m2 && m2[1].startsWith(newThemeName)) {
return true;
}
});
- if (found) {
+ if (newHref && newHref !== styleElem.href) {
styleElem.href = newHref;
}
}
diff --git a/src/librustdoc/html/static/scrape-examples-help.md b/src/librustdoc/html/static/scrape-examples-help.md
index 035b2e18b..002d19ec9 100644
--- a/src/librustdoc/html/static/scrape-examples-help.md
+++ b/src/librustdoc/html/static/scrape-examples-help.md
@@ -1,4 +1,4 @@
-Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
+Rustdoc will automatically scrape examples of documented items from a project's source code. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
```rust
// src/lib.rs
@@ -16,6 +16,7 @@ fn main() {
Then this code snippet will be included in the documentation for `a_func`.
+
## How to read scraped examples
Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking &varr; in the top-right. If a file contains multiple instances of an item, you can use the &pr; and &sc; buttons to toggle through each instance.
@@ -25,7 +26,7 @@ If there is more than one file that contains examples, then you should click "Mo
## How Rustdoc scrapes examples
-When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
+When you run `cargo doc -Zunstable-options -Zrustdoc-scrape-examples`, Rustdoc will analyze all the documented crates for uses of documented items. Then Rustdoc will include the source code of these instances in the generated documentation.
Rustdoc has a few techniques to ensure this doesn't overwhelm documentation readers, and that it doesn't blow up the page size:
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 75f2b7e35..b48b82307 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -2,167 +2,132 @@
//!
//! All the static files are included here for centralized access in case anything other than the
//! HTML rendering code (say, the theme checker) needs to access one of these files.
-//!
-//! Note about types: CSS and JavaScript files are included as `&'static str` to allow for the
-//! minifier to run on them. All other files are included as `&'static [u8]` so they can be
-//! directly written to a `Write` handle.
-
-/// The file contents of the main `rustdoc.css` file, responsible for the core layout of the page.
-pub(crate) static RUSTDOC_CSS: &str = include_str!("static/css/rustdoc.css");
-
-/// The file contents of `settings.css`, responsible for the items on the settings page.
-pub(crate) static SETTINGS_CSS: &str = include_str!("static/css/settings.css");
-
-/// The file contents of the `noscript.css` file, used in case JS isn't supported or is disabled.
-pub(crate) static NOSCRIPT_CSS: &str = include_str!("static/css/noscript.css");
-
-/// The file contents of `normalize.css`, included to even out standard elements between browser
-/// implementations.
-pub(crate) static NORMALIZE_CSS: &str = include_str!("static/css/normalize.css");
-
-/// The file contents of `main.js`, which contains the core JavaScript used on documentation pages,
-/// including search behavior and docblock folding, among others.
-pub(crate) static MAIN_JS: &str = include_str!("static/js/main.js");
-
-/// The file contents of `search.js`, which contains the search behavior.
-pub(crate) static SEARCH_JS: &str = include_str!("static/js/search.js");
-
-/// The file contents of `settings.js`, which contains the JavaScript used to handle the settings
-/// page.
-pub(crate) static SETTINGS_JS: &str = include_str!("static/js/settings.js");
-
-/// The file contents of `storage.js`, which contains functionality related to browser Local
-/// Storage, used to store documentation settings.
-pub(crate) static STORAGE_JS: &str = include_str!("static/js/storage.js");
-
-/// The file contents of `scraped-examples.js`, which contains functionality related to the
-/// --scrape-examples flag that inserts automatically-found examples of usages of items.
-pub(crate) static SCRAPE_EXAMPLES_JS: &str = include_str!("static/js/scrape-examples.js");
-
-pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
-
-/// The file contents of `wheel.svg`, the icon used for the settings button.
-pub(crate) static WHEEL_SVG: &[u8] = include_bytes!("static/images/wheel.svg");
-
-/// The file contents of `clipboard.svg`, the icon used for the "copy path" button.
-pub(crate) static CLIPBOARD_SVG: &[u8] = include_bytes!("static/images/clipboard.svg");
-
-/// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox.
-pub(crate) static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/images/down-arrow.svg");
-
-/// The file contents of `toggle-minus.svg`, the icon used for opened toggles.
-pub(crate) static TOGGLE_MINUS_PNG: &[u8] = include_bytes!("static/images/toggle-minus.svg");
-
-/// The file contents of `toggle-plus.svg`, the icon used for closed toggles.
-pub(crate) static TOGGLE_PLUS_PNG: &[u8] = include_bytes!("static/images/toggle-plus.svg");
-/// The contents of `COPYRIGHT.txt`, the license listing for files distributed with documentation
-/// output.
-pub(crate) static COPYRIGHT: &[u8] = include_bytes!("static/COPYRIGHT.txt");
+use rustc_data_structures::fx::FxHasher;
+use std::hash::Hasher;
+use std::path::{Path, PathBuf};
+use std::{fmt, str};
-/// The contents of `LICENSE-APACHE.txt`, the text of the Apache License, version 2.0.
-pub(crate) static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt");
-
-/// The contents of `LICENSE-MIT.txt`, the text of the MIT License.
-pub(crate) static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt");
-
-/// The contents of `rust-logo.svg`, the default icon of the documentation.
-pub(crate) static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg");
-
-/// The default documentation favicons (SVG and PNG fallbacks)
-pub(crate) static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg");
-pub(crate) static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png");
-pub(crate) static RUST_FAVICON_PNG_32: &[u8] = include_bytes!("static/images/favicon-32x32.png");
-
-/// The built-in themes given to every documentation site.
-pub(crate) mod themes {
- /// The "light" theme, selected by default when no setting is available. Used as the basis for
- /// the `--check-theme` functionality.
- pub(crate) static LIGHT: &str = include_str!("static/css/themes/light.css");
-
- /// The "dark" theme.
- pub(crate) static DARK: &str = include_str!("static/css/themes/dark.css");
-
- /// The "ayu" theme.
- pub(crate) static AYU: &str = include_str!("static/css/themes/ayu.css");
+pub(crate) struct StaticFile {
+ pub(crate) filename: PathBuf,
+ pub(crate) bytes: &'static [u8],
}
-/// Files related to the Fira Sans font.
-pub(crate) mod fira_sans {
- /// The file `FiraSans-Regular.woff2`, the Regular variant of the Fira Sans font in woff2.
- pub(crate) static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
-
- /// The file `FiraSans-Medium.woff2`, the Medium variant of the Fira Sans font in woff2.
- pub(crate) static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
-
- /// The file `FiraSans-LICENSE.txt`, the license text for the Fira Sans font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/FiraSans-LICENSE.txt");
+impl StaticFile {
+ fn new(filename: &str, bytes: &'static [u8]) -> StaticFile {
+ Self { filename: static_filename(filename, bytes), bytes }
+ }
+
+ pub(crate) fn minified(&self) -> Vec<u8> {
+ let extension = match self.filename.extension() {
+ Some(e) => e,
+ None => return self.bytes.to_owned(),
+ };
+ if extension == "css" {
+ minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into()
+ } else if extension == "js" {
+ minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into()
+ } else {
+ self.bytes.to_owned()
+ }
+ }
+
+ pub(crate) fn output_filename(&self) -> &Path {
+ &self.filename
+ }
}
-/// Files related to the Source Serif 4 font.
-pub(crate) mod source_serif_4 {
- /// The file `SourceSerif4-Regular.ttf.woff2`, the Regular variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static REGULAR: &[u8] =
- include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
-
- /// The file `SourceSerif4-Bold.ttf.woff2`, the Bold variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
-
- /// The file `SourceSerif4-It.ttf.woff2`, the Italic variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
-
- /// The file `SourceSerif4-LICENSE.txt`, the license text for the Source Serif 4 font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/SourceSerif4-LICENSE.md");
+/// The Display implementation for a StaticFile outputs its filename. This makes it
+/// convenient to interpolate static files into HTML templates.
+impl fmt::Display for StaticFile {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.output_filename().display())
+ }
}
-/// Files related to the Source Code Pro font.
-pub(crate) mod source_code_pro {
- /// The file `SourceCodePro-Regular.ttf.woff2`, the Regular variant of the Source Code Pro font
- /// in woff2.
- pub(crate) static REGULAR: &[u8] =
- include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
-
- /// The file `SourceCodePro-Semibold.ttf.woff2`, the Semibold variant of the Source Code Pro
- /// font in woff2.
- pub(crate) static SEMIBOLD: &[u8] =
- include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
-
- /// The file `SourceCodePro-It.ttf.woff2`, the Italic variant of the Source Code Pro font in
- /// woff2.
- pub(crate) static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
+/// Insert the provided suffix into a filename just before the extension.
+pub(crate) fn suffix_path(filename: &str, suffix: &str) -> PathBuf {
+ // We use splitn vs Path::extension here because we might get a filename
+ // like `style.min.css` and we want to process that into
+ // `style-suffix.min.css`. Path::extension would just return `css`
+ // which would result in `style.min-suffix.css` which isn't what we
+ // want.
+ let (base, ext) = filename.split_once('.').unwrap();
+ let filename = format!("{}{}.{}", base, suffix, ext);
+ filename.into()
+}
- /// The file `SourceCodePro-LICENSE.txt`, the license text of the Source Code Pro font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/SourceCodePro-LICENSE.txt");
+pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf {
+ let filename = filename.rsplit('/').next().unwrap();
+ suffix_path(filename, &static_suffix(contents))
}
-/// Files related to the Nanum Barun Gothic font.
-///
-/// These files are used to avoid some legacy CJK serif fonts in Windows.
-///
-/// Note that the Noto Sans KR font, which was used previously but was not very readable on Windows,
-/// has been replaced by the Nanum Barun Gothic font. This is due to Windows' implementation of font
-/// rendering that distorts OpenType fonts too much.
-///
-/// The font files were generated with these commands:
-///
-/// ```sh
-/// pyftsubset NanumBarunGothic.ttf \
-/// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
-/// --output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
-/// ```
-pub(crate) mod nanum_barun_gothic {
- /// The file `NanumBarunGothic.ttf.woff2`, the Regular variant of the Nanum Barun Gothic font.
- pub(crate) static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
+fn static_suffix(bytes: &[u8]) -> String {
+ let mut hasher = FxHasher::default();
+ hasher.write(bytes);
+ format!("-{:016x}", hasher.finish())
+}
- /// The file `NanumBarunGothic-LICENSE.txt`, the license text of the Nanum Barun Gothic font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/NanumBarunGothic-LICENSE.txt");
+macro_rules! static_files {
+ ($($field:ident => $file_path:literal,)+) => {
+ pub(crate) struct StaticFiles {
+ $(pub $field: StaticFile,)+
+ }
+
+ pub(crate) static STATIC_FILES: std::sync::LazyLock<StaticFiles> = std::sync::LazyLock::new(|| StaticFiles {
+ $($field: StaticFile::new($file_path, include_bytes!($file_path)),)+
+ });
+
+ pub(crate) fn for_each<E>(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> {
+ for sf in [
+ $(&STATIC_FILES.$field,)+
+ ] {
+ f(sf)?
+ }
+ Ok(())
+ }
+ }
}
-/// Files related to the sidebar in rustdoc sources.
-pub(crate) mod sidebar {
- /// File script to handle sidebar.
- pub(crate) static SOURCE_SCRIPT: &str = include_str!("static/js/source-script.js");
+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",
+ search_js => "static/js/search.js",
+ settings_js => "static/js/settings.js",
+ source_script_js => "static/js/source-script.js",
+ storage_js => "static/js/storage.js",
+ scrape_examples_js => "static/js/scrape-examples.js",
+ wheel_svg => "static/images/wheel.svg",
+ clipboard_svg => "static/images/clipboard.svg",
+ down_arrow_svg => "static/images/down-arrow.svg",
+ toggle_minus_png => "static/images/toggle-minus.svg",
+ toggle_plus_png => "static/images/toggle-plus.svg",
+ copyright => "static/COPYRIGHT.txt",
+ license_apache => "static/LICENSE-APACHE.txt",
+ license_mit => "static/LICENSE-MIT.txt",
+ rust_logo_svg => "static/images/rust-logo.svg",
+ 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",
+ source_serif_4_regular => "static/fonts/SourceSerif4-Regular.ttf.woff2",
+ source_serif_4_bold => "static/fonts/SourceSerif4-Bold.ttf.woff2",
+ source_serif_4_italic => "static/fonts/SourceSerif4-It.ttf.woff2",
+ source_serif_4_license => "static/fonts/SourceSerif4-LICENSE.md",
+ source_code_pro_regular => "static/fonts/SourceCodePro-Regular.ttf.woff2",
+ source_code_pro_semibold => "static/fonts/SourceCodePro-Semibold.ttf.woff2",
+ source_code_pro_italic => "static/fonts/SourceCodePro-It.ttf.woff2",
+ source_code_pro_license => "static/fonts/SourceCodePro-LICENSE.txt",
+ nanum_barun_gothic_regular => "static/fonts/NanumBarunGothic.ttf.woff2",
+ nanum_barun_gothic_license => "static/fonts/NanumBarunGothic-LICENSE.txt",
}
+
+pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index c32386916..aa3bf827d 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -7,48 +7,44 @@
<meta name="description" content="{{page.description}}"> {#- -#}
<meta name="keywords" content="{{page.keywords}}"> {#- -#}
<title>{{page.title}}</title> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Regular.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Regular.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Medium.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Regular.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Bold.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Semibold.ttf.woff2"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_regular}}"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_bold}}"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {#- -#}
<link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}normalize{{page.resource_suffix}}.css"> {#- -#}
+ href="{{static_root_path|safe}}{{files.normalize_css}}"> {#- -#}
<link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
+ href="{{static_root_path|safe}}{{files.rustdoc_css}}" {# -#}
id="mainThemeStyle"> {#- -#}
+ <link rel="stylesheet" id="themeStyle" href="{{static_root_path|safe}}{{files.theme_light_css}}"> {#- -#}
+ <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {#- -#}
+ <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_ayu_css}}"> {#- -#}
{%- for theme in themes -%}
- <link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}{{theme}}{{page.resource_suffix}}.css" {# -#}
- {%- if theme == "light" -%}
- id="themeStyle"
- {%- else -%}
- disabled
- {%- endif -%}
- >
+ <link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {#- -#}
{%- endfor -%}
<script id="default-settings" {# -#}
{% for (k, v) in layout.default_settings %}
data-{{k}}="{{v}}"
{%- endfor -%}
></script> {#- -#}
- <script src="{{static_root_path|safe}}storage{{page.resource_suffix}}.js"></script> {#- -#}
+ <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {#- -#}
{%- if page.css_class.contains("crate") -%}
<script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {#- -#}
{%- else if page.css_class == "source" -%}
- <script defer src="{{static_root_path|safe}}source-script{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.source_script_js}}"></script> {#- -#}
<script defer src="{{page.root_path|safe}}source-files{{page.resource_suffix}}.js"></script> {#- -#}
{%- else if !page.css_class.contains("mod") -%}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {#- -#}
{%- endif -%}
- <script defer src="{{static_root_path|safe}}main{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {#- -#}
{%- if layout.scrape_examples_extension -%}
- <script defer src="{{page.root_path|safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.scrape_examples_js}}"></script> {#- -#}
{%- endif -%}
<noscript> {#- -#}
<link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}noscript{{page.resource_suffix}}.css"> {#- -#}
+ href="{{static_root_path|safe}}{{files.noscript_css}}"> {#- -#}
</noscript> {#- -#}
{%- if layout.css_file_extension.is_some() -%}
<link rel="stylesheet" {# -#}
@@ -58,11 +54,11 @@
<link rel="icon" href="{{layout.favicon}}"> {#- -#}
{%- else -%}
<link rel="alternate icon" type="image/png" {# -#}
- href="{{static_root_path|safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_png_16}}"> {#- -#}
<link rel="alternate icon" type="image/png" {# -#}
- href="{{static_root_path|safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_png_32}}"> {#- -#}
<link rel="icon" type="image/svg+xml" {# -#}
- href="{{static_root_path|safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_svg}}"> {#- -#}
{%- endif -%}
{{- layout.external_html.in_header|safe -}}
</head> {#- -#}
@@ -81,7 +77,7 @@
{%- if !layout.logo.is_empty() -%}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</div> {#- -#}
</a> {#- -#}
@@ -95,7 +91,7 @@
{%- if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</div> {#- -#}
</a> {#- -#}
@@ -110,7 +106,7 @@
{%- if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</a> {#- -#}
{%- endif -%}
@@ -119,6 +115,7 @@
<input {# -#}
class="search-input" {# -#}
name="search" {# -#}
+ aria-label="Run search in the documentation" {# -#}
autocomplete="off" {# -#}
spellcheck="false" {# -#}
placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
@@ -129,7 +126,7 @@
<div id="settings-menu" tabindex="-1"> {#- -#}
<a href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#}
<img width="22" height="22" alt="Change settings" {# -#}
- src="{{static_root_path|safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+ src="{{static_root_path|safe}}{{files.wheel_svg}}"> {#- -#}
</a> {#- -#}
</div> {#- -#}
</form> {#- -#}
@@ -140,10 +137,14 @@
{{- layout.external_html.after_content|safe -}}
<div id="rustdoc-vars" {# -#}
data-root-path="{{page.root_path|safe}}" {# -#}
+ data-static-root-path="{{static_root_path|safe}}" {# -#}
data-current-crate="{{layout.krate}}" {# -#}
data-themes="{{themes|join(",") }}" {# -#}
data-resource-suffix="{{page.resource_suffix}}" {# -#}
data-rustdoc-version="{{rustdoc_version}}" {# -#}
+ data-search-js="{{files.search_js}}" {# -#}
+ data-settings-js="{{files.settings_js}}" {# -#}
+ data-settings-css="{{files.settings_css}}" {# -#}
> {#- -#}
</div> {#- -#}
</body> {#- -#}
diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html
index b6ce3ea3d..611d124d4 100644
--- a/src/librustdoc/html/templates/print_item.html
+++ b/src/librustdoc/html/templates/print_item.html
@@ -7,7 +7,7 @@
{%- endfor -%}
<a class="{{item_type}}" href="#">{{name}}</a> {#- -#}
<button id="copy-path" onclick="copy_path(this)" title="Copy item path to clipboard"> {#- -#}
- <img src="{{static_root_path|safe}}clipboard{{page.resource_suffix}}.svg" {# -#}
+ <img src="{{static_root_path|safe}}{{clipboard_svg}}" {# -#}
width="19" height="18" {# -#}
alt="Copy item path"> {#- -#}
</button> {#- -#}
@@ -21,8 +21,8 @@
<a class="srclink" href="{{href|safe}}">source</a> · {# -#}
{%- else -%}
{%- endmatch -%}
- <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> {#- -#}
- [<span class="inner">&#x2212;</span>] {#- -#}
- </a> {#- -#}
+ <button id="toggle-all-docs" title="collapse all docs"> {#- -#}
+ [<span>&#x2212;</span>] {#- -#}
+ </button> {#- -#}
</span> {#- -#}
</div> {#- -#}