-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple themes for rustdoc #47620
Multiple themes for rustdoc #47620
Changes from all commits
003b2bc
9aee164
aae6dc4
3c52acd
5f93159
5b85044
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<PathBuf>, | ||
} | ||
|
||
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<PathBuf>) -> 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<String> = 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might be signficantly nicer as an external file that's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but I need to generate the list of themes which can't be know at compile time. However, I intend to move most of this code into a js file later. |
||
var themePicker = document.getElementById("theme-picker"); | ||
themePicker.onclick = function() {{ | ||
if (themes.style.display === "block") {{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both branches should update |
||
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::<Vec<String>>() | ||
.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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <LICENSE-APACHE or | ||
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since rustdoc is on nightly anyway, why not just use ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make a conversion. I intended to use
?
but it cannot be used directly unfortunately...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't do the same as
?
- it returns anErr
instead ofNone
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't that how
?
works on anOption
inside something returningResult
? You get https://doc.rust-lang.org/stable/std/option/struct.NoneError.html, and you can do the appropriateFrom
impl if you want a custom thingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also takes the path of the file that caused the failure, which won't be there in the
NoneError
.(I also didn't realize that there was a conversion available between
NoneError
and custom error types>_>
)