mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-25 05:14:27 +00:00
Auto merge of #86157 - jsha:tera, r=jyn514,GuillaumeGomez
Use Tera templates for rustdoc. Replaces a format!() call in layout::render with a template expansion. Introduces a `templates` field in SharedContext so parts of rustdoc can share pre-rendered templates. This currently builds in a copy of the single template available, like with static files. However, future work can make this live-loadable with a perma-unstable flag, to make rustdoc developers' work easier. Part of #84419. Demo at https://hoffman-andrews.com/rust/tera/std/string/struct.String.html.
This commit is contained in:
commit
9d93819fa7
27
Cargo.lock
27
Cargo.lock
@ -1483,6 +1483,17 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globwalk"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gsgdt"
|
||||
version = "0.1.2"
|
||||
@ -4519,6 +4530,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"tera",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
@ -5100,6 +5112,21 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tera"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81060acb882480c8793782eb96bc86f5c83d2fc7175ad46c375c6956ef7afa62"
|
||||
dependencies = [
|
||||
"globwalk",
|
||||
"lazy_static",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.0.0"
|
||||
|
@ -21,6 +21,7 @@ regex = "1"
|
||||
rustdoc-json-types = { path = "../rustdoc-json-types" }
|
||||
tracing = "0.1"
|
||||
tracing-tree = "0.1.9"
|
||||
tera = { version = "1.10.0", default-features = false }
|
||||
|
||||
[dependencies.tracing-subscriber]
|
||||
version = "0.2.13"
|
||||
|
@ -4,7 +4,9 @@ use std::fs;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
crate struct ExternalHtml {
|
||||
/// Content that will be included inline in the <head> section of a
|
||||
/// rendered Markdown file or generated documentation
|
||||
|
@ -7,7 +7,9 @@ use crate::html::escape::Escape;
|
||||
use crate::html::format::{Buffer, Print};
|
||||
use crate::html::render::{ensure_trailing_slash, StylePath};
|
||||
|
||||
#[derive(Clone)]
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
crate struct Layout {
|
||||
crate logo: String,
|
||||
crate favicon: String,
|
||||
@ -22,6 +24,7 @@ crate struct Layout {
|
||||
crate generate_search_filter: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
crate struct Page<'a> {
|
||||
crate title: &'a str,
|
||||
crate css_class: &'a str,
|
||||
@ -40,7 +43,19 @@ impl<'a> Page<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PageLayout<'a> {
|
||||
static_root_path: &'a str,
|
||||
page: &'a Page<'a>,
|
||||
layout: &'a Layout,
|
||||
style_files: String,
|
||||
sidebar: String,
|
||||
content: String,
|
||||
krate_with_trailing_slash: String,
|
||||
}
|
||||
|
||||
crate fn render<T: Print, S: Print>(
|
||||
templates: &tera::Tera,
|
||||
layout: &Layout,
|
||||
page: &Page<'_>,
|
||||
sidebar: S,
|
||||
@ -48,184 +63,35 @@ crate fn render<T: Print, S: Print>(
|
||||
style_files: &[StylePath],
|
||||
) -> String {
|
||||
let static_root_path = page.get_static_root_path();
|
||||
format!(
|
||||
"<!DOCTYPE html>\
|
||||
<html lang=\"en\">\
|
||||
<head>\
|
||||
<meta charset=\"utf-8\">\
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
|
||||
<meta name=\"generator\" content=\"rustdoc\">\
|
||||
<meta name=\"description\" content=\"{description}\">\
|
||||
<meta name=\"keywords\" content=\"{keywords}\">\
|
||||
<title>{title}</title>\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}normalize{suffix}.css\">\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}rustdoc{suffix}.css\" \
|
||||
id=\"mainThemeStyle\">\
|
||||
{style_files}\
|
||||
<script id=\"default-settings\"{default_settings}></script>\
|
||||
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
|
||||
<script src=\"{root_path}crates{suffix}.js\"></script>\
|
||||
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
|
||||
{css_extension}\
|
||||
{favicon}\
|
||||
{in_header}\
|
||||
<style type=\"text/css\">\
|
||||
#crate-search{{background-image:url(\"{static_root_path}down-arrow{suffix}.svg\");}}\
|
||||
</style>\
|
||||
</head>\
|
||||
<body class=\"rustdoc {css_class}\">\
|
||||
<!--[if lte IE 11]>\
|
||||
<div class=\"warning\">\
|
||||
This old browser is unsupported and will most likely display funky \
|
||||
things.\
|
||||
</div>\
|
||||
<![endif]-->\
|
||||
{before_content}\
|
||||
<nav class=\"sidebar\">\
|
||||
<div class=\"sidebar-menu\" role=\"button\">☰</div>\
|
||||
{logo}\
|
||||
{sidebar}\
|
||||
</nav>\
|
||||
<div class=\"theme-picker\">\
|
||||
<button id=\"theme-picker\" aria-label=\"Pick another theme!\" aria-haspopup=\"menu\" title=\"themes\">\
|
||||
<img src=\"{static_root_path}brush{suffix}.svg\" \
|
||||
width=\"18\" height=\"18\" \
|
||||
alt=\"Pick another theme!\">\
|
||||
</button>\
|
||||
<div id=\"theme-choices\" role=\"menu\"></div>\
|
||||
</div>\
|
||||
<nav class=\"sub\">\
|
||||
<form class=\"search-form\">\
|
||||
<div class=\"search-container\">\
|
||||
<div>{filter_crates}\
|
||||
<input class=\"search-input\" name=\"search\" \
|
||||
disabled \
|
||||
autocomplete=\"off\" \
|
||||
spellcheck=\"false\" \
|
||||
placeholder=\"Click or press ‘S’ to search, ‘?’ for more options…\" \
|
||||
type=\"search\">\
|
||||
</div>\
|
||||
<button type=\"button\" id=\"help-button\" title=\"help\">?</button>\
|
||||
<a id=\"settings-menu\" href=\"{root_path}settings.html\" title=\"settings\">\
|
||||
<img src=\"{static_root_path}wheel{suffix}.svg\" \
|
||||
width=\"18\" height=\"18\" \
|
||||
alt=\"Change settings\">\
|
||||
</a>\
|
||||
</div>\
|
||||
</form>\
|
||||
</nav>\
|
||||
<section id=\"main\" class=\"content\">{content}</section>\
|
||||
<section id=\"search\" class=\"content hidden\"></section>\
|
||||
{after_content}\
|
||||
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
|
||||
data-search-index-js=\"{root_path}search-index{suffix}.js\" \
|
||||
data-search-js=\"{static_root_path}search{suffix}.js\"></div>\
|
||||
<script src=\"{static_root_path}main{suffix}.js\"></script>\
|
||||
{extra_scripts}\
|
||||
</body>\
|
||||
</html>",
|
||||
css_extension = if layout.css_file_extension.is_some() {
|
||||
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
|
||||
let style_files = style_files
|
||||
.iter()
|
||||
.filter_map(|t| {
|
||||
if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
|
||||
})
|
||||
.filter_map(|t| if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None })
|
||||
.map(|t| {
|
||||
format!(
|
||||
"<link rel=\"stylesheet\" \
|
||||
type=\"text/css\" \
|
||||
href=\"{static_root_path}theme{suffix}.css\">",
|
||||
static_root_path = static_root_path,
|
||||
suffix = page.resource_suffix
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
content = Buffer::html().to_display(t),
|
||||
static_root_path = static_root_path,
|
||||
root_path = page.root_path,
|
||||
css_class = page.css_class,
|
||||
logo = {
|
||||
if layout.logo.is_empty() {
|
||||
format!(
|
||||
"<a href='{root}{path}index.html'>\
|
||||
<div class='logo-container rust-logo'>\
|
||||
<img src='{static_root_path}rust-logo{suffix}.png' alt='logo'></div></a>",
|
||||
root = page.root_path,
|
||||
path = ensure_trailing_slash(&layout.krate),
|
||||
static_root_path = static_root_path,
|
||||
suffix = page.resource_suffix
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"<a href='{root}{path}index.html'>\
|
||||
<div class='logo-container'><img src='{logo}' alt='logo'></div></a>",
|
||||
root = page.root_path,
|
||||
path = ensure_trailing_slash(&layout.krate),
|
||||
logo = layout.logo
|
||||
)
|
||||
}
|
||||
},
|
||||
title = page.title,
|
||||
description = Escape(page.description),
|
||||
keywords = page.keywords,
|
||||
favicon = if layout.favicon.is_empty() {
|
||||
format!(
|
||||
r##"<link rel="icon" type="image/svg+xml" href="{static_root_path}favicon{suffix}.svg">
|
||||
<link rel="alternate icon" type="image/png" href="{static_root_path}favicon-16x16{suffix}.png">
|
||||
<link rel="alternate icon" type="image/png" href="{static_root_path}favicon-32x32{suffix}.png">"##,
|
||||
static_root_path = static_root_path,
|
||||
suffix = page.resource_suffix
|
||||
)
|
||||
} else {
|
||||
format!(r#"<link rel="shortcut icon" href="{}">"#, layout.favicon)
|
||||
},
|
||||
in_header = layout.external_html.in_header,
|
||||
before_content = layout.external_html.before_content,
|
||||
after_content = layout.external_html.after_content,
|
||||
sidebar = Buffer::html().to_display(sidebar),
|
||||
krate = layout.krate,
|
||||
default_settings = layout
|
||||
.default_settings
|
||||
.iter()
|
||||
.map(|(k, v)| format!(r#" data-{}="{}""#, k.replace('-', "_"), Escape(v)))
|
||||
.collect::<String>(),
|
||||
style_files = style_files
|
||||
.iter()
|
||||
.filter_map(|t| {
|
||||
if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
|
||||
})
|
||||
.filter_map(|t| {
|
||||
if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None }
|
||||
})
|
||||
.map(|t| format!(
|
||||
r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
|
||||
Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
|
||||
if t.1 { "disabled" } else { "" },
|
||||
if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
|
||||
))
|
||||
.collect::<String>(),
|
||||
suffix = page.resource_suffix,
|
||||
extra_scripts = page
|
||||
.static_extra_scripts
|
||||
.iter()
|
||||
.map(|e| {
|
||||
format!(
|
||||
"<script src=\"{static_root_path}{extra_script}.js\"></script>",
|
||||
static_root_path = static_root_path,
|
||||
extra_script = e
|
||||
)
|
||||
})
|
||||
.chain(page.extra_scripts.iter().map(|e| {
|
||||
format!(
|
||||
"<script src=\"{root_path}{extra_script}.js\"></script>",
|
||||
root_path = page.root_path,
|
||||
extra_script = e
|
||||
)
|
||||
}))
|
||||
.collect::<String>(),
|
||||
filter_crates = if layout.generate_search_filter {
|
||||
"<select id=\"crate-search\">\
|
||||
<option value=\"All crates\">All crates</option>\
|
||||
</select>"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)
|
||||
)
|
||||
})
|
||||
.collect::<String>();
|
||||
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
|
||||
let sidebar = Buffer::html().to_display(sidebar);
|
||||
let teractx = tera::Context::from_serialize(PageLayout {
|
||||
static_root_path,
|
||||
page,
|
||||
layout,
|
||||
style_files,
|
||||
sidebar,
|
||||
content,
|
||||
krate_with_trailing_slash,
|
||||
})
|
||||
.unwrap();
|
||||
templates.render("page.html", &teractx).unwrap()
|
||||
}
|
||||
|
||||
crate fn redirect(url: &str) -> String {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
@ -29,6 +30,7 @@ use crate::formats::FormatRenderer;
|
||||
use crate::html::escape::Escape;
|
||||
use crate::html::format::Buffer;
|
||||
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
|
||||
use crate::html::static_files::PAGE;
|
||||
use crate::html::{layout, sources};
|
||||
|
||||
/// Major driving force in all rustdoc rendering. This contains information
|
||||
@ -121,6 +123,8 @@ crate struct SharedContext<'tcx> {
|
||||
/// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
|
||||
/// the crate.
|
||||
redirections: Option<RefCell<FxHashMap<String, String>>>,
|
||||
|
||||
pub(crate) templates: tera::Tera,
|
||||
}
|
||||
|
||||
impl SharedContext<'_> {
|
||||
@ -218,6 +222,7 @@ impl<'tcx> Context<'tcx> {
|
||||
|
||||
if !self.render_redirect_pages {
|
||||
layout::render(
|
||||
&self.shared.templates,
|
||||
&self.shared.layout,
|
||||
&page,
|
||||
|buf: &mut _| print_sidebar(self, it, buf),
|
||||
@ -408,6 +413,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
let mut issue_tracker_base_url = None;
|
||||
let mut include_sources = true;
|
||||
|
||||
let mut templates = tera::Tera::default();
|
||||
templates.add_raw_template("page.html", PAGE).map_err(|e| Error {
|
||||
file: "page.html".into(),
|
||||
error: format!("{}: {}", e, e.source().map(|e| e.to_string()).unwrap_or_default()),
|
||||
})?;
|
||||
|
||||
// Crawl the crate attributes looking for attributes which control how we're
|
||||
// going to emit HTML
|
||||
for attr in krate.module.attrs.lists(sym::doc) {
|
||||
@ -454,6 +465,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
errors: receiver,
|
||||
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
|
||||
show_type_layout,
|
||||
templates,
|
||||
};
|
||||
|
||||
// Add the default themes to the `Vec` of stylepaths
|
||||
@ -540,6 +552,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
};
|
||||
let all = self.shared.all.replace(AllTypes::new());
|
||||
let v = layout::render(
|
||||
&self.shared.templates,
|
||||
&self.shared.layout,
|
||||
&page,
|
||||
sidebar,
|
||||
@ -557,6 +570,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
let sidebar = "<p class=\"location\">Settings</p><div class=\"sidebar-elems\"></div>";
|
||||
style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
|
||||
let v = layout::render(
|
||||
&self.shared.templates,
|
||||
&self.shared.layout,
|
||||
&page,
|
||||
sidebar,
|
||||
|
@ -460,7 +460,14 @@ pub(super) fn write_shared(
|
||||
})
|
||||
.collect::<String>()
|
||||
);
|
||||
let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
|
||||
let v = layout::render(
|
||||
&cx.shared.templates,
|
||||
&cx.shared.layout,
|
||||
&page,
|
||||
"",
|
||||
content,
|
||||
&cx.shared.style_files,
|
||||
);
|
||||
cx.shared.fs.write(&dst, v.as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +136,7 @@ impl SourceCollector<'_, 'tcx> {
|
||||
static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
|
||||
};
|
||||
let v = layout::render(
|
||||
&self.scx.templates,
|
||||
&self.scx.layout,
|
||||
&page,
|
||||
"",
|
||||
|
@ -64,6 +64,8 @@ crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/favicon.svg");
|
||||
crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/favicon-16x16.png");
|
||||
crate static RUST_FAVICON_PNG_32: &[u8] = include_bytes!("static/favicon-32x32.png");
|
||||
|
||||
crate static PAGE: &str = include_str!("templates/page.html");
|
||||
|
||||
/// The built-in themes given to every documentation site.
|
||||
crate mod themes {
|
||||
/// The "light" theme, selected by default when no setting is available. Used as the basis for
|
||||
|
37
src/librustdoc/html/templates/STYLE.md
Normal file
37
src/librustdoc/html/templates/STYLE.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Style for Templates
|
||||
|
||||
This directory has templates in the [Tera templating language](teradoc), which is very
|
||||
similar to [Jinja2](jinjadoc) and [Django](djangodoc) templates, and also to [Askama](askamadoc).
|
||||
|
||||
[teradoc]: https://tera.netlify.app/docs/#templates
|
||||
[jinjadoc]: https://jinja.palletsprojects.com/en/3.0.x/templates/
|
||||
[djangodoc]: https://docs.djangoproject.com/en/3.2/topics/templates/
|
||||
[askamadoc]: https://docs.rs/askama/0.10.5/askama/
|
||||
|
||||
We want our rendered output to have as little unnecessary whitespace as
|
||||
possible, so that pages load quickly. To achieve that we use Tera's
|
||||
[whitespace control] features. At the end of most lines, we put an empty comment
|
||||
tag with the whitespace control characters: `{#- -#}`. This causes all
|
||||
whitespace between the end of the line and the beginning of the next, including
|
||||
indentation, to be omitted on render. Sometimes we want to preserve a single
|
||||
space. In those cases we put the space at the end of the line, followed by
|
||||
`{# -#}`, which is a directive to remove following whitespace but not preceding.
|
||||
We also use the whitespace control characters in most instances of tags with
|
||||
control flow, for example `{%- if foo -%}`.
|
||||
|
||||
[whitespace control]: https://tera.netlify.app/docs/#whitespace-control
|
||||
|
||||
We want our templates to be readable, so we use indentation and newlines
|
||||
liberally. We indent by four spaces after opening an HTML tag _or_ a Tera
|
||||
tag. In most cases an HTML tag should be followed by a newline, but if the
|
||||
tag has simple contents and fits with its close tag on a single line, the
|
||||
contents don't necessarily need a new line.
|
||||
|
||||
Tera templates support quite sophisticated control flow. To keep our templates
|
||||
simple and understandable, we use only a subset: `if` and `for`. In particular
|
||||
we avoid [assignments in the template logic](assignments) and [Tera
|
||||
macros](macros). This also may make things easier if we switch to a different
|
||||
Jinja-style template system, like Askama, in the future.
|
||||
|
||||
[assignments]: https://tera.netlify.app/docs/#assignments
|
||||
[macros]: https://tera.netlify.app/docs/#macros
|
119
src/librustdoc/html/templates/page.html
Normal file
119
src/librustdoc/html/templates/page.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html> {#- -#}
|
||||
<html lang="en"> {#- -#}
|
||||
<head> {#- -#}
|
||||
<meta charset="utf-8"> {#- -#}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> {#- -#}
|
||||
<meta name="generator" content="rustdoc"> {#- -#}
|
||||
<meta name="description" content="{{page.description}}"> {#- -#}
|
||||
<meta name="keywords" content="{{page.keywords}}"> {#- -#}
|
||||
<title>{{page.title}}</title> {#- -#}
|
||||
<link rel="stylesheet" type="text/css" {# -#}
|
||||
href="{{static_root_path | safe}}normalize{{page.resource_suffix}}.css"> {#- -#}
|
||||
<link rel="stylesheet" type="text/css" {# -#}
|
||||
href="{{static_root_path | safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
|
||||
id="mainThemeStyle"> {#- -#}
|
||||
{{- style_files | safe -}}
|
||||
<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="{{page.root_path | safe}}crates{{page.resource_suffix}}.js"></script> {#- -#}
|
||||
<noscript> {#- -#}
|
||||
<link rel="stylesheet" {# -#}
|
||||
href="{{static_root_path | safe}}noscript{{page.resource_suffix}}.css"> {#- -#}
|
||||
</noscript> {#- -#}
|
||||
{%- if layout.css_file_extension -%}
|
||||
<link rel="stylesheet" type="text/css" {# -#}
|
||||
href="{{static_root_path | safe}}theme{{page.resource_suffix}}.css"> {#- -#}
|
||||
{%- endif -%}
|
||||
{%- if layout.favicon -%}
|
||||
<link rel="shortcut icon" href="{{layout.favicon}}"> {#- -#}
|
||||
{%- else -%}
|
||||
<link rel="icon" type="image/svg+xml" {# -#}
|
||||
href="{{static_root_path | safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
|
||||
<link rel="alternate icon" type="image/png" {# -#}
|
||||
href="{{static_root_path | safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#}
|
||||
<link rel="alternate icon" type="image/png" {# -#}
|
||||
href="{{static_root_path | safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#}
|
||||
{%- endif -%}
|
||||
{{- layout.external_html.in_header | safe -}}
|
||||
<style type="text/css"> {#- -#}
|
||||
#crate-search{ {#- -#}
|
||||
background-image:url("{{static_root_path | safe}}down-arrow{{page.resource_suffix}}.svg"); {#- -#}
|
||||
} {#- -#}
|
||||
</style> {#- -#}
|
||||
</head> {#- -#}
|
||||
<body class="rustdoc {{page.css_class}}"> {#- -#}
|
||||
<!--[if lte IE 11]> {#- -#}
|
||||
<div class="warning"> {#- -#}
|
||||
This old browser is unsupported and will most likely display funky things. {#- -#}
|
||||
</div> {#- -#}
|
||||
<![endif]--> {#- -#}
|
||||
{{- layout.external_html.before_content | safe -}}
|
||||
<nav class="sidebar"> {#- -#}
|
||||
<div class="sidebar-menu" role="button">☰</div> {#- -#}
|
||||
<a href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
|
||||
<div class='logo-container rust-logo'> {#- -#}
|
||||
<img src='
|
||||
{%- if layout.logo -%}
|
||||
{{layout.logo}}
|
||||
{%- else -%}
|
||||
{{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
|
||||
{%- endif -%}
|
||||
' alt='logo'> {#- -#}
|
||||
</div> {#- -#}
|
||||
</a> {#- -#}
|
||||
{{- sidebar | safe -}}
|
||||
</nav> {#- -#}
|
||||
<div class="theme-picker"> {#- -#}
|
||||
<button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
|
||||
<img width="18" height="18" alt="Pick another theme!" {# -#}
|
||||
src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
|
||||
</button> {#- -#}
|
||||
<div id="theme-choices" role="menu"></div> {#- -#}
|
||||
</div> {#- -#}
|
||||
<nav class="sub"> {#- -#}
|
||||
<form class="search-form"> {#- -#}
|
||||
<div class="search-container"> {#- -#}
|
||||
<div>{%- if layout.generate_search_filter -%}
|
||||
<select id="crate-search"> {#- -#}
|
||||
<option value="All crates">All crates</option> {#- -#}
|
||||
</select> {#- -#}
|
||||
{%- endif -%}
|
||||
<input {# -#}
|
||||
class="search-input"{# -#}
|
||||
name="search" {# -#}
|
||||
disabled {# -#}
|
||||
autocomplete="off" {# -#}
|
||||
spellcheck="false" {# -#}
|
||||
placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
|
||||
type="search"> {#- -#}
|
||||
</div> {#- -#}
|
||||
<button type="button" id="help-button" title="help">?</button> {#- -#}
|
||||
<a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
|
||||
<img width="18" height="18" alt="Change settings" {# -#}
|
||||
src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
|
||||
</a> {#- -#}
|
||||
</div> {#- -#}
|
||||
</form> {#- -#}
|
||||
</nav> {#- -#}
|
||||
<section id="main" class="content">{{- content | safe -}}</section> {#- -#}
|
||||
<section id="search" class="content hidden"></section> {#- -#}
|
||||
{{- layout.external_html.after_content | safe -}}
|
||||
<div id="rustdoc-vars" {# -#}
|
||||
data-root-path="{{page.root_path | safe}}" {# -#}
|
||||
data-current-crate="{{layout.krate}}" {# -#}
|
||||
data-search-index-js="{{page.root_path | safe}}search-index{{page.resource_suffix}}.js" {# -#}
|
||||
data-search-js="{{static_root_path | safe}}search{{page.resource_suffix}}.js"> {#- -#}
|
||||
</div>
|
||||
<script src="{{static_root_path | safe}}main{{page.resource_suffix}}.js"></script> {#- -#}
|
||||
{%- for script in page.static_extra_scripts -%}
|
||||
<script src="{{static_root_path | safe}}{{script}}.js"></script> {#- -#}
|
||||
{% endfor %}
|
||||
{%- for script in page.extra_scripts -%}
|
||||
<script src="{{page.root_path | safe}}{{script}}.js"></script> {#- -#}
|
||||
{% endfor %}
|
||||
</body> {#- -#}
|
||||
</html> {#- -#}
|
Loading…
Reference in New Issue
Block a user