@@ -176,6 +185,12 @@ r##"
after_content = layout.external_html.after_content,
sidebar = *sidebar,
krate = layout.krate,
+ themes = themes.iter()
+ .filter_map(|t| t.file_stem())
+ .filter_map(|t| t.to_str())
+ .map(|t| format!(r#"
"#,
+ page.root_path, t))
+ .collect::
(),
)
}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index b58a59f1217..e4878d6b631 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -132,6 +132,8 @@ pub struct SharedContext {
/// This flag indicates whether listings of modules (in the side bar and documentation itself)
/// should be ordered alphabetically or in order of appearance (in the source code).
pub sort_modules_alphabetically: bool,
+ /// Additional themes to be added to the generated docs.
+ pub themes: Vec,
}
impl SharedContext {
@@ -219,6 +221,17 @@ impl Error {
}
}
+macro_rules! try_none {
+ ($e:expr, $file:expr) => ({
+ use std::io;
+ match $e {
+ Some(e) => e,
+ None => return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"),
+ $file))
+ }
+ })
+}
+
macro_rules! try_err {
($e:expr, $file:expr) => ({
match $e {
@@ -489,7 +502,8 @@ pub fn run(mut krate: clean::Crate,
renderinfo: RenderInfo,
render_type: RenderType,
sort_modules_alphabetically: bool,
- deny_render_differences: bool) -> Result<(), Error> {
+ deny_render_differences: bool,
+ themes: Vec) -> Result<(), Error> {
let src_root = match krate.src {
FileName::Real(ref p) => match p.parent() {
Some(p) => p.to_path_buf(),
@@ -513,6 +527,7 @@ pub fn run(mut krate: clean::Crate,
markdown_warnings: RefCell::new(vec![]),
created_dirs: RefCell::new(FxHashSet()),
sort_modules_alphabetically,
+ themes,
};
// If user passed in `--playground-url` arg, we fill in crate name here
@@ -859,12 +874,65 @@ fn write_shared(cx: &Context,
// 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(cx.dst.join("main.js"),
- include_bytes!("static/main.js"))?;
write(cx.dst.join("rustdoc.css"),
include_bytes!("static/rustdoc.css"))?;
+
+ // To avoid "main.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: HashSet = HashSet::new();
+
+ for entry in &cx.shared.themes {
+ let mut content = Vec::with_capacity(100000);
+
+ let mut f = try_err!(File::open(&entry), &entry);
+ try_err!(f.read_to_end(&mut content), &entry);
+ write(cx.dst.join(try_none!(entry.file_name(), &entry)), content.as_slice())?;
+ themes.insert(try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry).to_owned());
+ }
+
+ write(cx.dst.join("brush.svg"),
+ include_bytes!("static/brush.svg"))?;
write(cx.dst.join("main.css"),
- include_bytes!("static/styles/main.css"))?;
+ include_bytes!("static/themes/main.css"))?;
+ themes.insert("main".to_owned());
+ write(cx.dst.join("dark.css"),
+ include_bytes!("static/themes/dark.css"))?;
+ themes.insert("dark".to_owned());
+
+ let mut themes: Vec<&String> = themes.iter().collect();
+ themes.sort();
+ // To avoid theme switch latencies as much as possible, we put everything theme related
+ // at the beginning of the html files into another js file.
+ write(cx.dst.join("theme.js"), format!(
+r#"var themes = document.getElementById("theme-choices");
+var themePicker = document.getElementById("theme-picker");
+themePicker.onclick = function() {{
+ if (themes.style.display === "block") {{
+ themes.style.display = "none";
+ themePicker.style.borderBottomRightRadius = "3px";
+ themePicker.style.borderBottomLeftRadius = "3px";
+ }} else {{
+ themes.style.display = "block";
+ themePicker.style.borderBottomRightRadius = "0";
+ themePicker.style.borderBottomLeftRadius = "0";
+ }}
+}};
+[{}].forEach(function(item) {{
+ var div = document.createElement('div');
+ div.innerHTML = item;
+ div.onclick = function(el) {{
+ switchTheme(currentTheme, mainTheme, item);
+ }};
+ themes.appendChild(div);
+}});
+"#, themes.iter()
+ .map(|s| format!("\"{}\"", s))
+ .collect::>()
+ .join(",")).as_bytes())?;
+
+ write(cx.dst.join("main.js"), include_bytes!("static/main.js"))?;
+ write(cx.dst.join("storage.js"), include_bytes!("static/storage.js"))?;
+
if let Some(ref css) = cx.shared.css_file_extension {
let out = cx.dst.join("theme.css");
try_err!(fs::copy(css, out), css);
@@ -1156,7 +1224,8 @@ impl<'a> SourceCollector<'a> {
};
layout::render(&mut w, &self.scx.layout,
&page, &(""), &Source(contents),
- self.scx.css_file_extension.is_some())?;
+ self.scx.css_file_extension.is_some(),
+ &self.scx.themes)?;
w.flush()?;
self.scx.local_sources.insert(p.clone(), href);
Ok(())
@@ -1520,7 +1589,8 @@ impl Context {
layout::render(writer, &self.shared.layout, &page,
&Sidebar{ cx: self, item: it },
&Item{ cx: self, item: it },
- self.shared.css_file_extension.is_some())?;
+ self.shared.css_file_extension.is_some(),
+ &self.shared.themes)?;
} else {
let mut url = self.root_path();
if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {
diff --git a/src/librustdoc/html/static/brush.svg b/src/librustdoc/html/static/brush.svg
new file mode 100644
index 00000000000..072264a6408
--- /dev/null
+++ b/src/librustdoc/html/static/brush.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index a9a5bd5de05..659bed18b00 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -122,6 +122,10 @@
}
}
document.getElementsByTagName("body")[0].style.marginTop = '45px';
+ var themePicker = document.getElementById("theme-picker");
+ if (themePicker) {
+ themePicker.style.position = "fixed";
+ }
}
function hideSidebar() {
@@ -136,6 +140,10 @@
filler.remove();
}
document.getElementsByTagName("body")[0].style.marginTop = '';
+ var themePicker = document.getElementById("theme-picker");
+ if (themePicker) {
+ themePicker.style.position = "absolute";
+ }
}
// used for special search precedence
@@ -1532,7 +1540,9 @@
ul.appendChild(li);
}
div.appendChild(ul);
- sidebar.appendChild(div);
+ if (sidebar) {
+ sidebar.appendChild(div);
+ }
}
block("primitive", "Primitive Types");
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 34b04de8673..c1ca86e3292 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -360,7 +360,8 @@ ul.item-list > li > .out-of-band {
}
h4 > code, h3 > code, .invisible > code {
- position: inherit;
+ max-width: calc(100% - 41px);
+ display: block;
}
.in-band, code {
@@ -376,6 +377,7 @@ h4 > code, h3 > code, .invisible > code {
margin: 0px;
padding: 0px;
display: inline-block;
+ max-width: calc(100% - 43px);
}
.in-band > code {
@@ -1140,3 +1142,37 @@ kbd {
border-radius: 3px;
box-shadow: inset 0 -1px 0;
}
+
+#theme-picker {
+ position: absolute;
+ left: 211px;
+ top: 17px;
+ padding: 4px;
+ border: 1px solid;
+ border-radius: 3px;
+ cursor: pointer;
+}
+
+#theme-choices {
+ display: none;
+ position: absolute;
+ left: -1px;
+ top: 30px;
+ border: 1px solid;
+ border-radius: 3px;
+ z-index: 1;
+}
+
+#theme-choices > div {
+ border-top: 1px solid;
+ padding: 4px;
+ text-align: center;
+}
+
+@media (max-width: 700px) {
+ #theme-picker {
+ left: 109px;
+ top: 7px;
+ z-index: 1;
+ }
+}
diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js
new file mode 100644
index 00000000000..0aa1065b378
--- /dev/null
+++ b/src/librustdoc/html/static/storage.js
@@ -0,0 +1,36 @@
+/*!
+ * Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+ * file at the top-level directory of this distribution and at
+ * http://rust-lang.org/COPYRIGHT.
+ *
+ * Licensed under the Apache License, Version 2.0 or the MIT license
+ * , at your
+ * option. This file may not be copied, modified, or distributed
+ * except according to those terms.
+ */
+
+var currentTheme = document.getElementById("themeStyle");
+var mainTheme = document.getElementById("mainThemeStyle");
+
+function updateLocalStorage(name, value) {
+ if (typeof(Storage) !== "undefined") {
+ localStorage[name] = value;
+ } else {
+ // No Web Storage support so we do nothing
+ }
+}
+
+function getCurrentValue(name) {
+ if (typeof(Storage) !== "undefined" && localStorage[name] !== undefined) {
+ return localStorage[name];
+ }
+ return null;
+}
+
+function switchTheme(styleElem, mainStyleElem, newTheme) {
+ styleElem.href = mainStyleElem.href.replace("rustdoc.css", newTheme + ".css");
+ updateLocalStorage('theme', newTheme);
+}
+
+switchTheme(currentTheme, mainTheme, getCurrentValue('theme') || 'main');
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
new file mode 100644
index 00000000000..05ac0660396
--- /dev/null
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -0,0 +1,384 @@
+/**
+ * Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+ * file at the top-level directory of this distribution and at
+ * http://rust-lang.org/COPYRIGHT.
+ *
+ * Licensed under the Apache License, Version 2.0 or the MIT license
+ * , at your
+ * option. This file may not be copied, modified, or distributed
+ * except according to those terms.
+ */
+
+body {
+ background-color: #353535;
+ color: #ddd;
+}
+
+h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
+ color: #ddd;
+}
+h1.fqn {
+ border-bottom-color: #d2d2d2;
+}
+h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
+ border-bottom-color: #d2d2d2;
+}
+
+.in-band {
+ background-color: #353535;
+}
+
+.invisible {
+ background: rgba(0, 0, 0, 0);
+}
+
+.docblock code, .docblock-short code {
+ background-color: #2A2A2A;
+}
+pre {
+ background-color: #2A2A2A;
+}
+
+.sidebar {
+ background-color: #505050;
+}
+
+.sidebar .current {
+ background-color: #333;
+}
+
+.source .sidebar {
+ background-color: #353535;
+}
+
+.sidebar .location {
+ border-color: #fff;
+ background: #575757;
+ color: #DDD;
+}
+
+.sidebar .version {
+ border-bottom-color: #DDD;
+}
+
+.sidebar-title {
+ border-top-color: #777;
+ border-bottom-color: #777;
+}
+
+.block a:hover {
+ background: #444;
+}
+
+.line-numbers span { color: #3B91E2; }
+.line-numbers .line-highlighted {
+ background-color: #0a042f !important;
+}
+
+.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
+ border-bottom-color: #DDD;
+}
+
+.docblock table {
+ border-color: #ddd;
+}
+
+.docblock table td {
+ border-top-color: #ddd;
+ border-bottom-color: #ddd;
+}
+
+.docblock table th {
+ border-top-color: #ddd;
+ border-bottom-color: #ddd;
+}
+
+:target { background: #494a3d; }
+
+:target > .in-band {
+ background: #494a3d;
+}
+
+.content .method .where,
+.content .fn .where,
+.content .where.fmt-newline {
+ color: #ddd;
+}
+
+.content .highlighted {
+ color: #eee !important;
+ background-color: #333;
+}
+.content .highlighted a, .content .highlighted span { color: #eee !important; }
+.content .highlighted.trait { background-color: #013191; }
+.content .highlighted.mod { background-color: #803a1b; }
+.content .highlighted.externcrate { background-color: #396bac; }
+.content .highlighted.enum { background-color: #5b4e68; }
+.content .highlighted.struct { background-color: #194e9f; }
+.content .highlighted.fn,
+.content .highlighted.method,
+.content .highlighted.tymethod { background-color: #4950ed; }
+.content .highlighted.type { background-color: #38902c; }
+.content .highlighted.foreigntype { background-color: #b200d6; }
+.content .highlighted.macro { background-color: #217d1c; }
+.content .highlighted.constant,
+.content .highlighted.static { background-color: #0063cc; }
+.content .highlighted.primitive { background-color: #00708a; }
+
+.content span.enum, .content a.enum, .block a.current.enum { color: #82b089; }
+.content span.struct, .content a.struct, .block a.current.struct { color: #ff794d; }
+.content span.type, .content a.type, .block a.current.type { color: #ff7f00; }
+.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #dd7de8; }
+.content span.macro, .content a.macro, .block a.current.macro { color: #09bd00; }
+.content span.union, .content a.union, .block a.current.union { color: #a6ae37; }
+.content span.constant, .content a.constant, .block a.current.constant,
+.content span.static, .content a.static, .block a.current.static { color: #82a5c9; }
+.content span.primitive, .content a.primitive, .block a.current.primitive { color: #43aec7; }
+.content span.externcrate,
+.content span.mod, .content a.mod, .block a.current.mod { color: #bda000; }
+.content span.trait, .content a.trait, .block a.current.trait { color: #b78cf2; }
+.content span.fn, .content a.fn, .block a.current.fn,
+.content span.method, .content a.method, .block a.current.method,
+.content span.tymethod, .content a.tymethod, .block a.current.tymethod,
+.content .fnname{ color: #2BAB63; }
+
+pre.rust .comment { color: #8d8d8b; }
+pre.rust .doccomment { color: #8ca375; }
+
+nav {
+ border-bottom-color: #4e4e4e;
+}
+nav.main .current {
+ border-top-color: #eee;
+ border-bottom-color: #eee;
+}
+nav.main .separator {
+ border-color: #eee;
+}
+a {
+ color: #ddd;
+}
+
+.docblock a, .docblock-short a, .stability a {
+ color: #D2991D;
+}
+
+a.test-arrow {
+ color: #dedede;
+}
+
+.collapse-toggle {
+ color: #999;
+}
+
+.search-input {
+ color: #111;
+ box-shadow: 0 0 0 1px #000, 0 0 0 2px transparent;
+ background-color: #f0f0f0;
+}
+
+.search-input:focus {
+ border-color: #008dfd;
+}
+
+.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #404040; }
+.stab.deprecated { background: #F3DFFF; border-color: #7F0087; color: #404040; }
+.stab.portability { background: #C4ECFF; border-color: #7BA5DB; color: #404040; }
+
+.module-item .stab {
+ color: #ddd;
+}
+
+#help > div {
+ background: #4d4d4d;
+ border-color: #bfbfbf;
+}
+
+#help dt {
+ border-color: #bfbfbf;
+ background: #fff;
+ color: black;
+}
+
+.since {
+ color: grey;
+}
+
+tr.result span.primitive::after {
+ color: #ddd;
+}
+
+.line-numbers :target { background-color: transparent; }
+
+/* Code highlighting */
+pre.rust .kw { color: #ab8ac1; }
+pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; }
+pre.rust .number, pre.rust .string { color: #83a300; }
+pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val,
+pre.rust .attribute, pre.rust .attribute .ident { color: #ee6868; }
+pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
+pre.rust .lifetime { color: #d97f26; }
+pre.rust .question-mark {
+ color: #ff9011;
+}
+
+a.test-arrow {
+ background-color: rgba(78, 139, 202, 0.2);
+}
+
+a.test-arrow:hover{
+ background-color: #4e8bca;
+}
+
+.toggle-label {
+ color: #999;
+}
+
+:target > code {
+ background: #FDFFD3;
+}
+
+pre.compile_fail {
+ border-left: 2px solid rgba(255,0,0,.6);
+}
+
+pre.compile_fail:hover, .information:hover + pre.compile_fail {
+ border-left: 2px solid #f00;
+}
+
+pre.ignore {
+ border-left: 2px solid rgba(255,142,0,.6);
+}
+
+pre.ignore:hover, .information:hover + pre.ignore {
+ border-left: 2px solid #ff9200;
+}
+
+.tooltip.compile_fail {
+ color: rgba(255,0,0,.6);
+}
+
+.information > .compile_fail:hover {
+ color: #f00;
+}
+
+.tooltip.ignore {
+ color: rgba(255,142,0,.6);
+}
+
+.information > .ignore:hover {
+ color: rgba(255,142,0,1);
+}
+
+.search-failed > a {
+ color: #0089ff;
+}
+
+.tooltip .tooltiptext {
+ background-color: black;
+ color: #fff;
+}
+
+.tooltip .tooltiptext::after {
+ border-color: transparent black transparent transparent;
+}
+
+.important-traits .tooltip .tooltiptext {
+ background-color: white;
+ color: black;
+ border-color: black;
+}
+
+#titles > div {
+ border-bottom-color: #ccc;
+}
+
+#titles > div.selected {
+ border-bottom-color: #0078ee;
+}
+
+#titles > div:hover {
+ border-bottom-color: #0089ff;
+}
+
+#titles > div > div.count {
+ color: #888;
+}
+
+.modal {
+ background-color: rgba(0,0,0,0.3);
+}
+
+.modal-content {
+ background-color: #272727;
+ border-color: #999;
+}
+
+.modal-content > .close {
+ background-color: #272727;
+ border-color: #999;
+}
+
+.modal-content > .close:hover {
+ background-color: #ff1f1f;
+ color: white;
+}
+
+.modal-content > .whiter {
+ background-color: #272727;
+}
+
+.modal-content > .close:hover + .whiter {
+ background-color: #ff1f1f;
+}
+
+@media (max-width: 700px) {
+ .sidebar-menu {
+ background-color: #505050;
+ border-bottom-color: #e0e0e0;
+ border-right-color: #e0e0e0;
+ }
+
+ .sidebar-elems {
+ background-color: #505050;
+ border-right-color: #000;
+ }
+
+ #sidebar-filler {
+ background-color: #505050;
+ border-bottom-color: #e0e0e0;
+ }
+}
+
+kbd {
+ color: #444d56;
+ background-color: #fafbfc;
+ border-color: #d1d5da;
+ border-bottom-color: #c6cbd1;
+ box-shadow-color: #c6cbd1;
+}
+
+#theme-picker {
+ border-color: #e0e0e0;
+ background: #f0f0f0;
+}
+
+#theme-choices {
+ border-color: #e0e0e0;
+ background-color: #353535;
+}
+
+#theme-choices > div {
+ border-top: #e0e0e0;
+}
+
+#theme-choices > div:hover {
+ background-color: #444;
+}
+
+@media (max-width: 700px) {
+ #theme-picker {
+ background: #353535;
+ }
+}
diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/themes/main.css
similarity index 96%
rename from src/librustdoc/html/static/styles/main.css
rename to src/librustdoc/html/static/themes/main.css
index bd740562424..84b21e7239f 100644
--- a/src/librustdoc/html/static/styles/main.css
+++ b/src/librustdoc/html/static/themes/main.css
@@ -351,3 +351,26 @@ kbd {
border-bottom-color: #c6cbd1;
box-shadow-color: #c6cbd1;
}
+
+#theme-picker {
+ border-color: #e0e0e0;
+}
+
+#theme-choices {
+ border-color: #ccc;
+ background-color: #fff;
+}
+
+#theme-choices > div {
+ border-top: #e0e0e0;
+}
+
+#theme-choices > div:hover {
+ background-color: #eee;
+}
+
+@media (max-width: 700px) {
+ #theme-picker {
+ background: #fff;
+ }
+}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 6347c4a58dd..ccd79e5b2c5 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -264,6 +264,11 @@ pub fn opts() -> Vec {
o.optflag("", "deny-render-differences", "abort doc runs when markdown rendering \
differences are found")
}),
+ unstable("themes", |o| {
+ o.optmulti("", "themes",
+ "additional themes which will be added to the generated docs",
+ "FILES")
+ }),
]
}
@@ -365,6 +370,15 @@ pub fn main_args(args: &[String]) -> isize {
}
}
+ let mut themes = Vec::new();
+ for theme in matches.opt_strs("themes").iter().map(|s| PathBuf::from(&s)) {
+ if !theme.is_file() {
+ eprintln!("rustdoc: option --themes arguments must all be files");
+ return 1;
+ }
+ themes.push(theme);
+ }
+
let external_html = match ExternalHtml::load(
&matches.opt_strs("html-in-header"),
&matches.opt_strs("html-before-content"),
@@ -413,7 +427,8 @@ pub fn main_args(args: &[String]) -> isize {
renderinfo,
render_type,
sort_modules_alphabetically,
- deny_render_differences)
+ deny_render_differences,
+ themes)
.expect("failed to generate documentation");
0
}