Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cd0f931

Browse files
committedJun 21, 2021
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.
1 parent 50a4072 commit cd0f931

File tree

10 files changed

+252
-176
lines changed

10 files changed

+252
-176
lines changed
 

‎Cargo.lock

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,17 @@ dependencies = [
14821482
"regex",
14831483
]
14841484

1485+
[[package]]
1486+
name = "globwalk"
1487+
version = "0.8.1"
1488+
source = "registry+https://github.com/rust-lang/crates.io-index"
1489+
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
1490+
dependencies = [
1491+
"bitflags",
1492+
"ignore",
1493+
"walkdir",
1494+
]
1495+
14851496
[[package]]
14861497
name = "gsgdt"
14871498
version = "0.1.2"
@@ -4518,6 +4529,7 @@ dependencies = [
45184529
"serde_json",
45194530
"smallvec",
45204531
"tempfile",
4532+
"tera",
45214533
"tracing",
45224534
"tracing-subscriber",
45234535
"tracing-tree",
@@ -5099,6 +5111,21 @@ dependencies = [
50995111
"utf-8",
51005112
]
51015113

5114+
[[package]]
5115+
name = "tera"
5116+
version = "1.10.0"
5117+
source = "registry+https://github.com/rust-lang/crates.io-index"
5118+
checksum = "81060acb882480c8793782eb96bc86f5c83d2fc7175ad46c375c6956ef7afa62"
5119+
dependencies = [
5120+
"globwalk",
5121+
"lazy_static",
5122+
"pest",
5123+
"pest_derive",
5124+
"regex",
5125+
"serde",
5126+
"serde_json",
5127+
]
5128+
51025129
[[package]]
51035130
name = "term"
51045131
version = "0.0.0"

‎src/librustdoc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ regex = "1"
2121
rustdoc-json-types = { path = "../rustdoc-json-types" }
2222
tracing = "0.1"
2323
tracing-tree = "0.1.9"
24+
tera = { version = "1.10.0", default-features = false }
2425

2526
[dependencies.tracing-subscriber]
2627
version = "0.2.13"

‎src/librustdoc/externalfiles.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use std::fs;
44
use std::path::Path;
55
use std::str;
66

7-
#[derive(Clone, Debug)]
7+
use serde::Serialize;
8+
9+
#[derive(Clone, Debug, Serialize)]
810
crate struct ExternalHtml {
911
/// Content that will be included inline in the <head> section of a
1012
/// rendered Markdown file or generated documentation

‎src/librustdoc/html/layout.rs

Lines changed: 40 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use crate::html::escape::Escape;
77
use crate::html::format::{Buffer, Print};
88
use crate::html::render::{ensure_trailing_slash, StylePath};
99

10-
#[derive(Clone)]
10+
use serde::Serialize;
11+
12+
#[derive(Clone, Serialize)]
1113
crate struct Layout {
1214
crate logo: String,
1315
crate favicon: String,
@@ -22,6 +24,7 @@ crate struct Layout {
2224
crate generate_search_filter: bool,
2325
}
2426

27+
#[derive(Serialize)]
2528
crate struct Page<'a> {
2629
crate title: &'a str,
2730
crate css_class: &'a str,
@@ -40,192 +43,55 @@ impl<'a> Page<'a> {
4043
}
4144
}
4245

46+
#[derive(Serialize)]
47+
struct PageLayout<'a> {
48+
static_root_path: &'a str,
49+
page: &'a Page<'a>,
50+
layout: &'a Layout,
51+
style_files: String,
52+
sidebar: String,
53+
content: String,
54+
krate_with_trailing_slash: String,
55+
}
56+
4357
crate fn render<T: Print, S: Print>(
58+
templates: &tera::Tera,
4459
layout: &Layout,
4560
page: &Page<'_>,
4661
sidebar: S,
4762
t: T,
4863
style_files: &[StylePath],
4964
) -> String {
5065
let static_root_path = page.get_static_root_path();
51-
format!(
52-
"<!DOCTYPE html>\
53-
<html lang=\"en\">\
54-
<head>\
55-
<meta charset=\"utf-8\">\
56-
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
57-
<meta name=\"generator\" content=\"rustdoc\">\
58-
<meta name=\"description\" content=\"{description}\">\
59-
<meta name=\"keywords\" content=\"{keywords}\">\
60-
<title>{title}</title>\
61-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}normalize{suffix}.css\">\
62-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}rustdoc{suffix}.css\" \
63-
id=\"mainThemeStyle\">\
64-
{style_files}\
65-
<script id=\"default-settings\"{default_settings}></script>\
66-
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
67-
<script src=\"{root_path}crates{suffix}.js\"></script>\
68-
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
69-
{css_extension}\
70-
{favicon}\
71-
{in_header}\
72-
<style type=\"text/css\">\
73-
#crate-search{{background-image:url(\"{static_root_path}down-arrow{suffix}.svg\");}}\
74-
</style>\
75-
</head>\
76-
<body class=\"rustdoc {css_class}\">\
77-
<!--[if lte IE 11]>\
78-
<div class=\"warning\">\
79-
This old browser is unsupported and will most likely display funky \
80-
things.\
81-
</div>\
82-
<![endif]-->\
83-
{before_content}\
84-
<nav class=\"sidebar\">\
85-
<div class=\"sidebar-menu\" role=\"button\">&#9776;</div>\
86-
{logo}\
87-
{sidebar}\
88-
</nav>\
89-
<div class=\"theme-picker\">\
90-
<button id=\"theme-picker\" aria-label=\"Pick another theme!\" aria-haspopup=\"menu\" title=\"themes\">\
91-
<img src=\"{static_root_path}brush{suffix}.svg\" \
92-
width=\"18\" height=\"18\" \
93-
alt=\"Pick another theme!\">\
94-
</button>\
95-
<div id=\"theme-choices\" role=\"menu\"></div>\
96-
</div>\
97-
<nav class=\"sub\">\
98-
<form class=\"search-form\">\
99-
<div class=\"search-container\">\
100-
<div>{filter_crates}\
101-
<input class=\"search-input\" name=\"search\" \
102-
disabled \
103-
autocomplete=\"off\" \
104-
spellcheck=\"false\" \
105-
placeholder=\"Click or press ‘S’ to search, ‘?’ for more options…\" \
106-
type=\"search\">\
107-
</div>\
108-
<button type=\"button\" id=\"help-button\" title=\"help\">?</button>\
109-
<a id=\"settings-menu\" href=\"{root_path}settings.html\" title=\"settings\">\
110-
<img src=\"{static_root_path}wheel{suffix}.svg\" \
111-
width=\"18\" height=\"18\" \
112-
alt=\"Change settings\">\
113-
</a>\
114-
</div>\
115-
</form>\
116-
</nav>\
117-
<section id=\"main\" class=\"content\">{content}</section>\
118-
<section id=\"search\" class=\"content hidden\"></section>\
119-
{after_content}\
120-
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
121-
data-search-index-js=\"{root_path}search-index{suffix}.js\" \
122-
data-search-js=\"{static_root_path}search{suffix}.js\"></div>\
123-
<script src=\"{static_root_path}main{suffix}.js\"></script>\
124-
{extra_scripts}\
125-
</body>\
126-
</html>",
127-
css_extension = if layout.css_file_extension.is_some() {
66+
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
67+
let style_files = style_files
68+
.iter()
69+
.filter_map(|t| {
70+
if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
71+
})
72+
.filter_map(|t| if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None })
73+
.map(|t| {
12874
format!(
129-
"<link rel=\"stylesheet\" \
130-
type=\"text/css\" \
131-
href=\"{static_root_path}theme{suffix}.css\">",
132-
static_root_path = static_root_path,
133-
suffix = page.resource_suffix
134-
)
135-
} else {
136-
String::new()
137-
},
138-
content = Buffer::html().to_display(t),
139-
static_root_path = static_root_path,
140-
root_path = page.root_path,
141-
css_class = page.css_class,
142-
logo = {
143-
if layout.logo.is_empty() {
144-
format!(
145-
"<a href='{root}{path}index.html'>\
146-
<div class='logo-container rust-logo'>\
147-
<img src='{static_root_path}rust-logo{suffix}.png' alt='logo'></div></a>",
148-
root = page.root_path,
149-
path = ensure_trailing_slash(&layout.krate),
150-
static_root_path = static_root_path,
151-
suffix = page.resource_suffix
152-
)
153-
} else {
154-
format!(
155-
"<a href='{root}{path}index.html'>\
156-
<div class='logo-container'><img src='{logo}' alt='logo'></div></a>",
157-
root = page.root_path,
158-
path = ensure_trailing_slash(&layout.krate),
159-
logo = layout.logo
160-
)
161-
}
162-
},
163-
title = page.title,
164-
description = Escape(page.description),
165-
keywords = page.keywords,
166-
favicon = if layout.favicon.is_empty() {
167-
format!(
168-
r##"<link rel="icon" type="image/svg+xml" href="{static_root_path}favicon{suffix}.svg">
169-
<link rel="alternate icon" type="image/png" href="{static_root_path}favicon-16x16{suffix}.png">
170-
<link rel="alternate icon" type="image/png" href="{static_root_path}favicon-32x32{suffix}.png">"##,
171-
static_root_path = static_root_path,
172-
suffix = page.resource_suffix
173-
)
174-
} else {
175-
format!(r#"<link rel="shortcut icon" href="{}">"#, layout.favicon)
176-
},
177-
in_header = layout.external_html.in_header,
178-
before_content = layout.external_html.before_content,
179-
after_content = layout.external_html.after_content,
180-
sidebar = Buffer::html().to_display(sidebar),
181-
krate = layout.krate,
182-
default_settings = layout
183-
.default_settings
184-
.iter()
185-
.map(|(k, v)| format!(r#" data-{}="{}""#, k.replace('-', "_"), Escape(v)))
186-
.collect::<String>(),
187-
style_files = style_files
188-
.iter()
189-
.filter_map(|t| {
190-
if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
191-
})
192-
.filter_map(|t| {
193-
if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None }
194-
})
195-
.map(|t| format!(
19675
r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
19776
Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
19877
if t.1 { "disabled" } else { "" },
19978
if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
200-
))
201-
.collect::<String>(),
202-
suffix = page.resource_suffix,
203-
extra_scripts = page
204-
.static_extra_scripts
205-
.iter()
206-
.map(|e| {
207-
format!(
208-
"<script src=\"{static_root_path}{extra_script}.js\"></script>",
209-
static_root_path = static_root_path,
210-
extra_script = e
211-
)
212-
})
213-
.chain(page.extra_scripts.iter().map(|e| {
214-
format!(
215-
"<script src=\"{root_path}{extra_script}.js\"></script>",
216-
root_path = page.root_path,
217-
extra_script = e
218-
)
219-
}))
220-
.collect::<String>(),
221-
filter_crates = if layout.generate_search_filter {
222-
"<select id=\"crate-search\">\
223-
<option value=\"All crates\">All crates</option>\
224-
</select>"
225-
} else {
226-
""
227-
},
228-
)
79+
)
80+
})
81+
.collect::<String>();
82+
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
83+
let sidebar = Buffer::html().to_display(sidebar);
84+
let teractx = tera::Context::from_serialize(PageLayout {
85+
static_root_path,
86+
page,
87+
layout,
88+
style_files,
89+
sidebar,
90+
content,
91+
krate_with_trailing_slash,
92+
})
93+
.unwrap();
94+
templates.render("page.html", &teractx).unwrap()
22995
}
23096

23197
crate fn redirect(url: &str) -> String {

‎src/librustdoc/html/render/context.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::cell::RefCell;
22
use std::collections::BTreeMap;
3+
use std::error::Error as StdError;
34
use std::io;
45
use std::path::{Path, PathBuf};
56
use std::rc::Rc;
@@ -29,6 +30,7 @@ use crate::formats::FormatRenderer;
2930
use crate::html::escape::Escape;
3031
use crate::html::format::Buffer;
3132
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
33+
use crate::html::static_files::PAGE;
3234
use crate::html::{layout, sources};
3335

3436
/// Major driving force in all rustdoc rendering. This contains information
@@ -121,6 +123,8 @@ crate struct SharedContext<'tcx> {
121123
/// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
122124
/// the crate.
123125
redirections: Option<RefCell<FxHashMap<String, String>>>,
126+
127+
pub(crate) templates: tera::Tera,
124128
}
125129

126130
impl SharedContext<'_> {
@@ -218,6 +222,7 @@ impl<'tcx> Context<'tcx> {
218222

219223
if !self.render_redirect_pages {
220224
layout::render(
225+
&self.shared.templates,
221226
&self.shared.layout,
222227
&page,
223228
|buf: &mut _| print_sidebar(self, it, buf),
@@ -408,6 +413,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
408413
let mut issue_tracker_base_url = None;
409414
let mut include_sources = true;
410415

416+
let mut templates = tera::Tera::default();
417+
templates.add_raw_template("page.html", PAGE).map_err(|e| Error {
418+
file: "page.html".into(),
419+
error: format!("{}: {}", e, e.source().map(|e| e.to_string()).unwrap_or_default()),
420+
})?;
421+
411422
// Crawl the crate attributes looking for attributes which control how we're
412423
// going to emit HTML
413424
for attr in krate.module.attrs.lists(sym::doc) {
@@ -454,6 +465,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
454465
errors: receiver,
455466
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
456467
show_type_layout,
468+
templates,
457469
};
458470

459471
// Add the default themes to the `Vec` of stylepaths
@@ -540,6 +552,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
540552
};
541553
let all = self.shared.all.replace(AllTypes::new());
542554
let v = layout::render(
555+
&self.shared.templates,
543556
&self.shared.layout,
544557
&page,
545558
sidebar,
@@ -557,6 +570,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
557570
let sidebar = "<p class=\"location\">Settings</p><div class=\"sidebar-elems\"></div>";
558571
style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
559572
let v = layout::render(
573+
&self.shared.templates,
560574
&self.shared.layout,
561575
&page,
562576
sidebar,

‎src/librustdoc/html/render/write_shared.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,14 @@ pub(super) fn write_shared(
460460
})
461461
.collect::<String>()
462462
);
463-
let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
463+
let v = layout::render(
464+
&cx.shared.templates,
465+
&cx.shared.layout,
466+
&page,
467+
"",
468+
content,
469+
&cx.shared.style_files,
470+
);
464471
cx.shared.fs.write(&dst, v.as_bytes())?;
465472
}
466473
}

‎src/librustdoc/html/sources.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl SourceCollector<'_, 'tcx> {
136136
static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
137137
};
138138
let v = layout::render(
139+
&self.scx.templates,
139140
&self.scx.layout,
140141
&page,
141142
"",

‎src/librustdoc/html/static_files.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/favicon.svg");
6464
crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/favicon-16x16.png");
6565
crate static RUST_FAVICON_PNG_32: &[u8] = include_bytes!("static/favicon-32x32.png");
6666

67+
crate static PAGE: &str = include_str!("templates/page.html");
68+
6769
/// The built-in themes given to every documentation site.
6870
crate mod themes {
6971
/// The "light" theme, selected by default when no setting is available. Used as the basis for
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Style for Templates
2+
3+
This directory has templates in the [Tera templating language](teradoc), which is very
4+
similar to [Jinja2](jinjadoc) and [Django](djangodoc) templates, and also to [Askama](askamadoc).
5+
6+
[teradoc]: https://tera.netlify.app/docs/#templates
7+
[jinjadoc]: https://jinja.palletsprojects.com/en/3.0.x/templates/
8+
[djangodoc]: https://docs.djangoproject.com/en/3.2/topics/templates/
9+
[askamadoc]: https://docs.rs/askama/0.10.5/askama/
10+
11+
We want our rendered output to have as little unnecessary whitespace as
12+
possible, so that pages load quickly. To achieve that we use Tera's
13+
[whitespace control] features. At the end of most lines, we put an empty comment
14+
tag with the whitespace control characters: `{#- -#}`. This causes all
15+
whitespace between the end of the line and the beginning of the next, including
16+
indentation, to be omitted on render. Sometimes we want to preserve a single
17+
space. In those cases we put the space at the end of the line, followed by
18+
`{# -#}`, which is a directive to remove following whitespace but not preceding.
19+
We also use the whitespace control characters in most instances of tags with
20+
control flow, for example `{%- if foo -%}`.
21+
22+
[whitespace control]: https://tera.netlify.app/docs/#whitespace-control
23+
24+
We want our templates to be readable, so we use indentation and newlines
25+
liberally. We indent by four spaces after opening an HTML tag _or_ a Tera
26+
tag. In most cases an HTML tag should be followed by a newline, but if the
27+
tag has simple contents and fits with its close tag on a single line, the
28+
contents don't necessarily need a new line.
29+
30+
Tera templates support quite sophisticated control flow. To keep our templates
31+
simple and understandable, we use only a subset: `if` and `for`. In particular
32+
we avoid [assignments in the template logic](assignments) and [Tera
33+
macros](macros). This also may make things easier if we switch to a different
34+
Jinja-style template system, like Askama, in the future.
35+
36+
[assignments]: https://tera.netlify.app/docs/#assignments
37+
[macros]: https://tera.netlify.app/docs/#macros
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<!DOCTYPE html> {#- -#}
2+
<html lang="en"> {#- -#}
3+
<head> {#- -#}
4+
<meta charset="utf-8"> {#- -#}
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0"> {#- -#}
6+
<meta name="generator" content="rustdoc"> {#- -#}
7+
<meta name="description" content="{{page.description}}"> {#- -#}
8+
<meta name="keywords" content="{{page.keywords}}"> {#- -#}
9+
<title>{{page.title}}</title> {#- -#}
10+
<link rel="stylesheet" type="text/css" {# -#}
11+
href="{{static_root_path | safe}}normalize{{page.resource_suffix}}.css"> {#- -#}
12+
<link rel="stylesheet" type="text/css" {# -#}
13+
href="{{static_root_path | safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
14+
id="mainThemeStyle"> {#- -#}
15+
{{- style_files | safe -}}
16+
<script id="default-settings" {# -#}
17+
{% for k, v in layout.default_settings %}
18+
data-{{k}}="{{v}}"
19+
{%- endfor -%}
20+
></script> {#- -#}
21+
<script src="{{static_root_path | safe}}storage{{page.resource_suffix}}.js"></script> {#- -#}
22+
<script src="{{page.root_path | safe}}crates{{page.resource_suffix}}.js"></script> {#- -#}
23+
<noscript> {#- -#}
24+
<link rel="stylesheet" {# -#}
25+
href="{{static_root_path | safe}}noscript{{page.resource_suffix}}.css"> {#- -#}
26+
</noscript> {#- -#}
27+
{%- if layout.css_file_extension -%}
28+
<link rel="stylesheet" type="text/css" {# -#}
29+
href="{{static_root_path | safe}}theme{{page.resource_suffix}}.css"> {#- -#}
30+
{%- endif -%}
31+
{%- if layout.favicon -%}
32+
<link rel="shortcut icon" href="{{layout.favicon}}"> {#- -#}
33+
{%- else -%}
34+
<link rel="icon" type="image/svg+xml" {# -#}
35+
href="{{static_root_path | safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
36+
<link rel="alternate icon" type="image/png" {# -#}
37+
href="{{static_root_path | safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#}
38+
<link rel="alternate icon" type="image/png" {# -#}
39+
href="{{static_root_path | safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#}
40+
{%- endif -%}
41+
{{- layout.external_html.in_header | safe -}}
42+
<style type="text/css"> {#- -#}
43+
#crate-search{ {#- -#}
44+
background-image:url("{{static_root_path | safe}}down-arrow{{page.resource_suffix}}.svg"); {#- -#}
45+
} {#- -#}
46+
</style> {#- -#}
47+
</head> {#- -#}
48+
<body class="rustdoc {{page.css_class}}"> {#- -#}
49+
<!--[if lte IE 11]> {#- -#}
50+
<div class="warning"> {#- -#}
51+
This old browser is unsupported and will most likely display funky things. {#- -#}
52+
</div> {#- -#}
53+
<![endif]--> {#- -#}
54+
{{- layout.external_html.before_content | safe -}}
55+
<nav class="sidebar"> {#- -#}
56+
<div class="sidebar-menu" role="button">&#9776;</div> {#- -#}
57+
<a href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
58+
<div class='logo-container rust-logo'> {#- -#}
59+
<img src='
60+
{%- if layout.logo -%}
61+
{{layout.logo}}
62+
{%- else -%}
63+
{{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
64+
{%- endif -%}
65+
' alt='logo'> {#- -#}
66+
</div> {#- -#}
67+
</a> {#- -#}
68+
{{- sidebar | safe -}}
69+
</nav> {#- -#}
70+
<div class="theme-picker"> {#- -#}
71+
<button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
72+
<img width="18" height="18" alt="Pick another theme!" {# -#}
73+
src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
74+
</button> {#- -#}
75+
<div id="theme-choices" role="menu"></div> {#- -#}
76+
</div> {#- -#}
77+
<nav class="sub"> {#- -#}
78+
<form class="search-form"> {#- -#}
79+
<div class="search-container"> {#- -#}
80+
<div>{%- if layout.generate_search_filter -%}
81+
<select id="crate-search"> {#- -#}
82+
<option value="All crates">All crates</option> {#- -#}
83+
</select> {#- -#}
84+
{%- endif -%}
85+
<input {# -#}
86+
class="search-input"{# -#}
87+
name="search" {# -#}
88+
disabled {# -#}
89+
autocomplete="off" {# -#}
90+
spellcheck="false" {# -#}
91+
placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
92+
type="search"> {#- -#}
93+
</div> {#- -#}
94+
<button type="button" id="help-button" title="help">?</button> {#- -#}
95+
<a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
96+
<img width="18" height="18" alt="Change settings" {# -#}
97+
src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
98+
</a> {#- -#}
99+
</div> {#- -#}
100+
</form> {#- -#}
101+
</nav> {#- -#}
102+
<section id="main" class="content">{{- content | safe -}}</section> {#- -#}
103+
<section id="search" class="content hidden"></section> {#- -#}
104+
{{- layout.external_html.after_content | safe -}}
105+
<div id="rustdoc-vars" {# -#}
106+
data-root-path="{{page.root_path | safe}}" {# -#}
107+
data-current-crate="{{layout.krate}}" {# -#}
108+
data-search-index-js="{{page.root_path | safe}}search-index{{page.resource_suffix}}.js" {# -#}
109+
data-search-js="{{static_root_path | safe}}search{{page.resource_suffix}}.js"> {#- -#}
110+
</div>
111+
<script src="{{static_root_path | safe}}main{{page.resource_suffix}}.js"></script> {#- -#}
112+
{%- for script in page.static_extra_scripts -%}
113+
<script src="{{static_root_path | safe}}{{script}}.js"></script> {#- -#}
114+
{% endfor %}
115+
{%- for script in page.extra_scripts -%}
116+
<script src="{{page.root_path | safe}}{{script}}.js"></script> {#- -#}
117+
{% endfor %}
118+
</body> {#- -#}
119+
</html> {#- -#}

0 commit comments

Comments
 (0)
Please sign in to comment.