Skip to content

Commit 3a39b2a

Browse files
committed
Auto merge of #47620 - GuillaumeGomez:multiple-themes, r=QuietMisdreavus
Multiple themes for rustdoc r? @QuietMisdreavus
2 parents 48a7ea9 + 5b85044 commit 3a39b2a

File tree

10 files changed

+603
-13
lines changed

10 files changed

+603
-13
lines changed

src/librustdoc/externalfiles.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::str;
1414
use html::markdown::{Markdown, RenderType};
1515

1616
#[derive(Clone)]
17-
pub struct ExternalHtml{
17+
pub struct ExternalHtml {
1818
/// Content that will be included inline in the <head> section of a
1919
/// rendered Markdown file or generated documentation
2020
pub in_header: String,

src/librustdoc/html/layout.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use std::fmt;
1212
use std::io;
13+
use std::path::PathBuf;
1314

1415
use externalfiles::ExternalHtml;
1516

@@ -31,7 +32,7 @@ pub struct Page<'a> {
3132

3233
pub fn render<T: fmt::Display, S: fmt::Display>(
3334
dst: &mut io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T,
34-
css_file_extension: bool)
35+
css_file_extension: bool, themes: &[PathBuf])
3536
-> io::Result<()>
3637
{
3738
write!(dst,
@@ -47,8 +48,11 @@ r##"<!DOCTYPE html>
4748
<title>{title}</title>
4849
4950
<link rel="stylesheet" type="text/css" href="{root_path}normalize.css">
50-
<link rel="stylesheet" type="text/css" href="{root_path}rustdoc.css">
51-
<link rel="stylesheet" type="text/css" href="{root_path}main.css">
51+
<link rel="stylesheet" type="text/css" href="{root_path}rustdoc.css" id="mainThemeStyle">
52+
{themes}
53+
<link rel="stylesheet" type="text/css" href="{root_path}dark.css">
54+
<link rel="stylesheet" type="text/css" href="{root_path}main.css" id="themeStyle">
55+
<script src="{root_path}storage.js"></script>
5256
{css_extension}
5357
5458
{favicon}
@@ -70,6 +74,11 @@ r##"<!DOCTYPE html>
7074
{sidebar}
7175
</nav>
7276
77+
<button id="theme-picker">
78+
<img src="{root_path}brush.svg" width="18" alt="Pick another theme!">
79+
<div id="theme-choices"></div>
80+
</button>
81+
<script src="{root_path}theme.js"></script>
7382
<nav class="sub">
7483
<form class="search-form js-only">
7584
<div class="search-container">
@@ -176,6 +185,12 @@ r##"<!DOCTYPE html>
176185
after_content = layout.external_html.after_content,
177186
sidebar = *sidebar,
178187
krate = layout.krate,
188+
themes = themes.iter()
189+
.filter_map(|t| t.file_stem())
190+
.filter_map(|t| t.to_str())
191+
.map(|t| format!(r#"<link rel="stylesheet" type="text/css" href="{}{}">"#,
192+
page.root_path, t))
193+
.collect::<String>(),
179194
)
180195
}
181196

src/librustdoc/html/render.rs

+76-6
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ pub struct SharedContext {
132132
/// This flag indicates whether listings of modules (in the side bar and documentation itself)
133133
/// should be ordered alphabetically or in order of appearance (in the source code).
134134
pub sort_modules_alphabetically: bool,
135+
/// Additional themes to be added to the generated docs.
136+
pub themes: Vec<PathBuf>,
135137
}
136138

137139
impl SharedContext {
@@ -219,6 +221,17 @@ impl Error {
219221
}
220222
}
221223

224+
macro_rules! try_none {
225+
($e:expr, $file:expr) => ({
226+
use std::io;
227+
match $e {
228+
Some(e) => e,
229+
None => return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"),
230+
$file))
231+
}
232+
})
233+
}
234+
222235
macro_rules! try_err {
223236
($e:expr, $file:expr) => ({
224237
match $e {
@@ -489,7 +502,8 @@ pub fn run(mut krate: clean::Crate,
489502
renderinfo: RenderInfo,
490503
render_type: RenderType,
491504
sort_modules_alphabetically: bool,
492-
deny_render_differences: bool) -> Result<(), Error> {
505+
deny_render_differences: bool,
506+
themes: Vec<PathBuf>) -> Result<(), Error> {
493507
let src_root = match krate.src {
494508
FileName::Real(ref p) => match p.parent() {
495509
Some(p) => p.to_path_buf(),
@@ -513,6 +527,7 @@ pub fn run(mut krate: clean::Crate,
513527
markdown_warnings: RefCell::new(vec![]),
514528
created_dirs: RefCell::new(FxHashSet()),
515529
sort_modules_alphabetically,
530+
themes,
516531
};
517532

518533
// If user passed in `--playground-url` arg, we fill in crate name here
@@ -859,12 +874,65 @@ fn write_shared(cx: &Context,
859874
// Add all the static files. These may already exist, but we just
860875
// overwrite them anyway to make sure that they're fresh and up-to-date.
861876

862-
write(cx.dst.join("main.js"),
863-
include_bytes!("static/main.js"))?;
864877
write(cx.dst.join("rustdoc.css"),
865878
include_bytes!("static/rustdoc.css"))?;
879+
880+
// To avoid "main.css" to be overwritten, we'll first run over the received themes and only
881+
// then we'll run over the "official" styles.
882+
let mut themes: HashSet<String> = HashSet::new();
883+
884+
for entry in &cx.shared.themes {
885+
let mut content = Vec::with_capacity(100000);
886+
887+
let mut f = try_err!(File::open(&entry), &entry);
888+
try_err!(f.read_to_end(&mut content), &entry);
889+
write(cx.dst.join(try_none!(entry.file_name(), &entry)), content.as_slice())?;
890+
themes.insert(try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry).to_owned());
891+
}
892+
893+
write(cx.dst.join("brush.svg"),
894+
include_bytes!("static/brush.svg"))?;
866895
write(cx.dst.join("main.css"),
867-
include_bytes!("static/styles/main.css"))?;
896+
include_bytes!("static/themes/main.css"))?;
897+
themes.insert("main".to_owned());
898+
write(cx.dst.join("dark.css"),
899+
include_bytes!("static/themes/dark.css"))?;
900+
themes.insert("dark".to_owned());
901+
902+
let mut themes: Vec<&String> = themes.iter().collect();
903+
themes.sort();
904+
// To avoid theme switch latencies as much as possible, we put everything theme related
905+
// at the beginning of the html files into another js file.
906+
write(cx.dst.join("theme.js"), format!(
907+
r#"var themes = document.getElementById("theme-choices");
908+
var themePicker = document.getElementById("theme-picker");
909+
themePicker.onclick = function() {{
910+
if (themes.style.display === "block") {{
911+
themes.style.display = "none";
912+
themePicker.style.borderBottomRightRadius = "3px";
913+
themePicker.style.borderBottomLeftRadius = "3px";
914+
}} else {{
915+
themes.style.display = "block";
916+
themePicker.style.borderBottomRightRadius = "0";
917+
themePicker.style.borderBottomLeftRadius = "0";
918+
}}
919+
}};
920+
[{}].forEach(function(item) {{
921+
var div = document.createElement('div');
922+
div.innerHTML = item;
923+
div.onclick = function(el) {{
924+
switchTheme(currentTheme, mainTheme, item);
925+
}};
926+
themes.appendChild(div);
927+
}});
928+
"#, themes.iter()
929+
.map(|s| format!("\"{}\"", s))
930+
.collect::<Vec<String>>()
931+
.join(",")).as_bytes())?;
932+
933+
write(cx.dst.join("main.js"), include_bytes!("static/main.js"))?;
934+
write(cx.dst.join("storage.js"), include_bytes!("static/storage.js"))?;
935+
868936
if let Some(ref css) = cx.shared.css_file_extension {
869937
let out = cx.dst.join("theme.css");
870938
try_err!(fs::copy(css, out), css);
@@ -1156,7 +1224,8 @@ impl<'a> SourceCollector<'a> {
11561224
};
11571225
layout::render(&mut w, &self.scx.layout,
11581226
&page, &(""), &Source(contents),
1159-
self.scx.css_file_extension.is_some())?;
1227+
self.scx.css_file_extension.is_some(),
1228+
&self.scx.themes)?;
11601229
w.flush()?;
11611230
self.scx.local_sources.insert(p.clone(), href);
11621231
Ok(())
@@ -1520,7 +1589,8 @@ impl Context {
15201589
layout::render(writer, &self.shared.layout, &page,
15211590
&Sidebar{ cx: self, item: it },
15221591
&Item{ cx: self, item: it },
1523-
self.shared.css_file_extension.is_some())?;
1592+
self.shared.css_file_extension.is_some(),
1593+
&self.shared.themes)?;
15241594
} else {
15251595
let mut url = self.root_path();
15261596
if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {

src/librustdoc/html/static/brush.svg

+1
Loading

src/librustdoc/html/static/main.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@
122122
}
123123
}
124124
document.getElementsByTagName("body")[0].style.marginTop = '45px';
125+
var themePicker = document.getElementById("theme-picker");
126+
if (themePicker) {
127+
themePicker.style.position = "fixed";
128+
}
125129
}
126130

127131
function hideSidebar() {
@@ -136,6 +140,10 @@
136140
filler.remove();
137141
}
138142
document.getElementsByTagName("body")[0].style.marginTop = '';
143+
var themePicker = document.getElementById("theme-picker");
144+
if (themePicker) {
145+
themePicker.style.position = "absolute";
146+
}
139147
}
140148

141149
// used for special search precedence
@@ -1532,7 +1540,9 @@
15321540
ul.appendChild(li);
15331541
}
15341542
div.appendChild(ul);
1535-
sidebar.appendChild(div);
1543+
if (sidebar) {
1544+
sidebar.appendChild(div);
1545+
}
15361546
}
15371547

15381548
block("primitive", "Primitive Types");

src/librustdoc/html/static/rustdoc.css

+37-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,8 @@ ul.item-list > li > .out-of-band {
360360
}
361361

362362
h4 > code, h3 > code, .invisible > code {
363-
position: inherit;
363+
max-width: calc(100% - 41px);
364+
display: block;
364365
}
365366

366367
.in-band, code {
@@ -376,6 +377,7 @@ h4 > code, h3 > code, .invisible > code {
376377
margin: 0px;
377378
padding: 0px;
378379
display: inline-block;
380+
max-width: calc(100% - 43px);
379381
}
380382

381383
.in-band > code {
@@ -1140,3 +1142,37 @@ kbd {
11401142
border-radius: 3px;
11411143
box-shadow: inset 0 -1px 0;
11421144
}
1145+
1146+
#theme-picker {
1147+
position: absolute;
1148+
left: 211px;
1149+
top: 17px;
1150+
padding: 4px;
1151+
border: 1px solid;
1152+
border-radius: 3px;
1153+
cursor: pointer;
1154+
}
1155+
1156+
#theme-choices {
1157+
display: none;
1158+
position: absolute;
1159+
left: -1px;
1160+
top: 30px;
1161+
border: 1px solid;
1162+
border-radius: 3px;
1163+
z-index: 1;
1164+
}
1165+
1166+
#theme-choices > div {
1167+
border-top: 1px solid;
1168+
padding: 4px;
1169+
text-align: center;
1170+
}
1171+
1172+
@media (max-width: 700px) {
1173+
#theme-picker {
1174+
left: 109px;
1175+
top: 7px;
1176+
z-index: 1;
1177+
}
1178+
}

src/librustdoc/html/static/storage.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*!
2+
* Copyright 2018 The Rust Project Developers. See the COPYRIGHT
3+
* file at the top-level directory of this distribution and at
4+
* http://rust-lang.org/COPYRIGHT.
5+
*
6+
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
7+
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8+
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
9+
* option. This file may not be copied, modified, or distributed
10+
* except according to those terms.
11+
*/
12+
13+
var currentTheme = document.getElementById("themeStyle");
14+
var mainTheme = document.getElementById("mainThemeStyle");
15+
16+
function updateLocalStorage(name, value) {
17+
if (typeof(Storage) !== "undefined") {
18+
localStorage[name] = value;
19+
} else {
20+
// No Web Storage support so we do nothing
21+
}
22+
}
23+
24+
function getCurrentValue(name) {
25+
if (typeof(Storage) !== "undefined" && localStorage[name] !== undefined) {
26+
return localStorage[name];
27+
}
28+
return null;
29+
}
30+
31+
function switchTheme(styleElem, mainStyleElem, newTheme) {
32+
styleElem.href = mainStyleElem.href.replace("rustdoc.css", newTheme + ".css");
33+
updateLocalStorage('theme', newTheme);
34+
}
35+
36+
switchTheme(currentTheme, mainTheme, getCurrentValue('theme') || 'main');

0 commit comments

Comments
 (0)