Auto merge of #54733 - GuillaumeGomez:stabilize-rustdoc-theme, r=ollie27,Dylan-DPC
Stabilize rustdoc theme options Closes #54730 This PR stabilizes the `--themes` (now `--theme`) and `--theme-checker` (now `--check-theme`) options, for allowing users to add custom themes to their documentation. Rustdoc includes two themes by default: `light` and `dark`. Using the `--theme` option, you can give rustdoc a CSS file to include as an extra theme for that render. Themes are named after the CSS file used, so using `--theme /path/to/your/custom-theme.css` will add a theme called `custom-theme` to the documentation. Even though the CLI flag to add a theme is getting stabilized, there's no guarantee that a theme file will always have the same effect on documentation generated with future versions of rustdoc. To aid in ensuring that a theme will work, the flag `--check-theme` is also available, which compares the CSS rules defined by a custom theme against the ones used in the `light` theme. If the `light` theme defines a CSS rule that the custom theme does not, rustdoc will report an error. (Rustdoc also performs this check for themes given to `--theme`, but only reports a warning when a difference is found.)
This commit is contained in:
commit
3e525e3f6d
12 changed files with 88 additions and 52 deletions
|
@ -355,7 +355,38 @@ $ rustdoc src/lib.rs --edition 2018
|
||||||
$ rustdoc --test src/lib.rs --edition 2018
|
$ rustdoc --test src/lib.rs --edition 2018
|
||||||
```
|
```
|
||||||
|
|
||||||
This flag allows rustdoc to treat your rust code as the given edition. It will compile doctests with
|
This flag allows `rustdoc` to treat your rust code as the given edition. It will compile doctests with
|
||||||
the given edition as well. As with `rustc`, the default edition that `rustdoc` will use is `2015`
|
the given edition as well. As with `rustc`, the default edition that `rustdoc` will use is `2015`
|
||||||
(the first edition).
|
(the first edition).
|
||||||
|
|
||||||
|
## `--theme`: add a theme to the documentation output
|
||||||
|
|
||||||
|
Using this flag looks like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ rustdoc src/lib.rs --theme /path/to/your/custom-theme.css
|
||||||
|
```
|
||||||
|
|
||||||
|
`rustdoc`'s default output includes two themes: `light` (the default) and
|
||||||
|
`dark`. This flag allows you to add custom themes to the output. Giving a CSS
|
||||||
|
file to this flag adds it to your documentation as an additional theme choice.
|
||||||
|
The theme's name is determined by its filename; a theme file named
|
||||||
|
`custom-theme.css` will add a theme named `custom-theme` to the documentation.
|
||||||
|
|
||||||
|
## `--check-theme`: verify custom themes against the default theme
|
||||||
|
|
||||||
|
Using this flag looks like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ rustdoc --check-theme /path/to/your/custom-theme.css
|
||||||
|
```
|
||||||
|
|
||||||
|
While `rustdoc`'s HTML output is more-or-less consistent between versions, there
|
||||||
|
is no guarantee that a theme file will have the same effect. The `--theme` flag
|
||||||
|
will still allow you to add the theme to your documentation, but to ensure that
|
||||||
|
your theme works as expected, you can use this flag to verify that it implements
|
||||||
|
the same CSS rules as the official `light` theme.
|
||||||
|
|
||||||
|
`--check-theme` is a separate mode in `rustdoc`. When `rustdoc` sees the
|
||||||
|
`--check-theme` flag, it discards all other flags and only performs the CSS rule
|
||||||
|
comparison operation.
|
||||||
|
|
|
@ -294,30 +294,6 @@ some consideration for their stability, and names that end in a number). Giving
|
||||||
`rustdoc` will disable this sorting and instead make it print the items in the order they appear in
|
`rustdoc` will disable this sorting and instead make it print the items in the order they appear in
|
||||||
the source.
|
the source.
|
||||||
|
|
||||||
### `--themes`: provide additional themes
|
|
||||||
|
|
||||||
Using this flag looks like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ rustdoc src/lib.rs -Z unstable-options --themes theme.css
|
|
||||||
```
|
|
||||||
|
|
||||||
Giving this flag to `rustdoc` will make it copy your theme into the generated crate docs and enable
|
|
||||||
it in the theme selector. Note that `rustdoc` will reject your theme file if it doesn't style
|
|
||||||
everything the "light" theme does. See `--theme-checker` below for details.
|
|
||||||
|
|
||||||
### `--theme-checker`: verify theme CSS for validity
|
|
||||||
|
|
||||||
Using this flag looks like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ rustdoc -Z unstable-options --theme-checker theme.css
|
|
||||||
```
|
|
||||||
|
|
||||||
Before including your theme in crate docs, `rustdoc` will compare all the CSS rules it contains
|
|
||||||
against the "light" theme included by default. Using this flag will allow you to see which rules are
|
|
||||||
missing if `rustdoc` rejects your theme.
|
|
||||||
|
|
||||||
### `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs
|
### `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs
|
||||||
|
|
||||||
Using this flag looks like this:
|
Using this flag looks like this:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -281,12 +282,12 @@ impl Options {
|
||||||
// check for deprecated options
|
// check for deprecated options
|
||||||
check_deprecated_options(&matches, &diag);
|
check_deprecated_options(&matches, &diag);
|
||||||
|
|
||||||
let to_check = matches.opt_strs("theme-checker");
|
let to_check = matches.opt_strs("check-theme");
|
||||||
if !to_check.is_empty() {
|
if !to_check.is_empty() {
|
||||||
let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
|
let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
|
||||||
let mut errors = 0;
|
let mut errors = 0;
|
||||||
|
|
||||||
println!("rustdoc: [theme-checker] Starting tests!");
|
println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)");
|
||||||
for theme_file in to_check.iter() {
|
for theme_file in to_check.iter() {
|
||||||
print!(" - Checking \"{}\"...", theme_file);
|
print!(" - Checking \"{}\"...", theme_file);
|
||||||
let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
|
let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
|
||||||
|
@ -357,22 +358,34 @@ impl Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut themes = Vec::new();
|
let mut themes = Vec::new();
|
||||||
if matches.opt_present("themes") {
|
if matches.opt_present("theme") {
|
||||||
let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
|
let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
|
||||||
|
|
||||||
for (theme_file, theme_s) in matches.opt_strs("themes")
|
for (theme_file, theme_s) in matches.opt_strs("theme")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| (PathBuf::from(&s), s.to_owned())) {
|
.map(|s| (PathBuf::from(&s), s.to_owned())) {
|
||||||
if !theme_file.is_file() {
|
if !theme_file.is_file() {
|
||||||
diag.struct_err("option --themes arguments must all be files").emit();
|
diag.struct_err(&format!("invalid argument: \"{}\"", theme_s))
|
||||||
|
.help("arguments to --theme must be files")
|
||||||
|
.emit();
|
||||||
|
return Err(1);
|
||||||
|
}
|
||||||
|
if theme_file.extension() != Some(OsStr::new("css")) {
|
||||||
|
diag.struct_err(&format!("invalid argument: \"{}\"", theme_s))
|
||||||
|
.emit();
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
|
let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
|
||||||
if !success || !ret.is_empty() {
|
if !success {
|
||||||
diag.struct_err(&format!("invalid theme: \"{}\"", theme_s))
|
diag.struct_err(&format!("error loading theme file: \"{}\"", theme_s)).emit();
|
||||||
.help("check what's wrong with the --theme-checker option")
|
|
||||||
.emit();
|
|
||||||
return Err(1);
|
return Err(1);
|
||||||
|
} else if !ret.is_empty() {
|
||||||
|
diag.struct_warn(&format!("theme file \"{}\" is missing CSS rules from the \
|
||||||
|
default theme", theme_s))
|
||||||
|
.warn("the theme may appear incorrect when loaded")
|
||||||
|
.help(&format!("to see what rules are missing, call `rustdoc \
|
||||||
|
--check-theme \"{}\"`", theme_s))
|
||||||
|
.emit();
|
||||||
}
|
}
|
||||||
themes.push(theme_file);
|
themes.push(theme_file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::externalfiles::ExternalHtml;
|
use crate::externalfiles::ExternalHtml;
|
||||||
|
use crate::html::escape::Escape;
|
||||||
use crate::html::render::ensure_trailing_slash;
|
use crate::html::render::ensure_trailing_slash;
|
||||||
use crate::html::format::{Buffer, Print};
|
use crate::html::format::{Buffer, Print};
|
||||||
|
|
||||||
|
@ -166,10 +167,11 @@ pub fn render<T: Print, S: Print>(
|
||||||
themes = themes.iter()
|
themes = themes.iter()
|
||||||
.filter_map(|t| t.file_stem())
|
.filter_map(|t| t.file_stem())
|
||||||
.filter_map(|t| t.to_str())
|
.filter_map(|t| t.to_str())
|
||||||
.map(|t| format!(r#"<link rel="stylesheet" type="text/css" href="{}{}{}.css">"#,
|
.map(|t| format!(r#"<link rel="stylesheet" type="text/css" href="{}.css">"#,
|
||||||
static_root_path,
|
Escape(&format!("{}{}{}",
|
||||||
t,
|
static_root_path,
|
||||||
page.resource_suffix))
|
t,
|
||||||
|
page.resource_suffix))))
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
suffix=page.resource_suffix,
|
suffix=page.resource_suffix,
|
||||||
static_extra_scripts=page.static_extra_scripts.iter().map(|e| {
|
static_extra_scripts=page.static_extra_scripts.iter().map(|e| {
|
||||||
|
|
|
@ -633,19 +633,16 @@ function handleThemeButtonsBlur(e) {{
|
||||||
|
|
||||||
themePicker.onclick = switchThemeButtonState;
|
themePicker.onclick = switchThemeButtonState;
|
||||||
themePicker.onblur = handleThemeButtonsBlur;
|
themePicker.onblur = handleThemeButtonsBlur;
|
||||||
[{}].forEach(function(item) {{
|
{}.forEach(function(item) {{
|
||||||
var but = document.createElement('button');
|
var but = document.createElement('button');
|
||||||
but.innerHTML = item;
|
but.textContent = item;
|
||||||
but.onclick = function(el) {{
|
but.onclick = function(el) {{
|
||||||
switchTheme(currentTheme, mainTheme, item, true);
|
switchTheme(currentTheme, mainTheme, item, true);
|
||||||
}};
|
}};
|
||||||
but.onblur = handleThemeButtonsBlur;
|
but.onblur = handleThemeButtonsBlur;
|
||||||
themes.appendChild(but);
|
themes.appendChild(but);
|
||||||
}});"#,
|
}});"#,
|
||||||
themes.iter()
|
as_json(&themes));
|
||||||
.map(|s| format!("\"{}\"", s))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(","));
|
|
||||||
write(cx.dst.join(&format!("theme{}.js", cx.shared.resource_suffix)),
|
write(cx.dst.join(&format!("theme{}.js", cx.shared.resource_suffix)),
|
||||||
theme_js.as_bytes()
|
theme_js.as_bytes()
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub static RUST_FAVICON: &'static [u8] = include_bytes!("static/favicon.ico");
|
||||||
/// The built-in themes given to every documentation site.
|
/// The built-in themes given to every documentation site.
|
||||||
pub mod themes {
|
pub mod themes {
|
||||||
/// The "light" theme, selected by default when no setting is available. Used as the basis for
|
/// The "light" theme, selected by default when no setting is available. Used as the basis for
|
||||||
/// the `--theme-checker` functionality.
|
/// the `--check-theme` functionality.
|
||||||
pub static LIGHT: &'static str = include_str!("static/themes/light.css");
|
pub static LIGHT: &'static str = include_str!("static/themes/light.css");
|
||||||
|
|
||||||
/// The "dark" theme.
|
/// The "dark" theme.
|
||||||
|
|
|
@ -252,13 +252,13 @@ fn opts() -> Vec<RustcOptGroup> {
|
||||||
o.optflag("", "sort-modules-by-appearance", "sort modules by where they appear in the \
|
o.optflag("", "sort-modules-by-appearance", "sort modules by where they appear in the \
|
||||||
program, rather than alphabetically")
|
program, rather than alphabetically")
|
||||||
}),
|
}),
|
||||||
unstable("themes", |o| {
|
stable("theme", |o| {
|
||||||
o.optmulti("", "themes",
|
o.optmulti("", "theme",
|
||||||
"additional themes which will be added to the generated docs",
|
"additional themes which will be added to the generated docs",
|
||||||
"FILES")
|
"FILES")
|
||||||
}),
|
}),
|
||||||
unstable("theme-checker", |o| {
|
stable("check-theme", |o| {
|
||||||
o.optmulti("", "theme-checker",
|
o.optmulti("", "check-theme",
|
||||||
"check if given theme is valid",
|
"check if given theme is valid",
|
||||||
"FILES")
|
"FILES")
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -273,6 +273,7 @@ pub fn test_theme_against<P: AsRef<Path>>(
|
||||||
diag: &Handler,
|
diag: &Handler,
|
||||||
) -> (bool, Vec<String>) {
|
) -> (bool, Vec<String>) {
|
||||||
let data = try_something!(fs::read(f), diag, (false, vec![]));
|
let data = try_something!(fs::read(f), diag, (false, vec![]));
|
||||||
|
|
||||||
let paths = load_css_paths(&data);
|
let paths = load_css_paths(&data);
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
get_differences(against, &paths, &mut ret);
|
get_differences(against, &paths, &mut ret);
|
||||||
|
|
10
src/test/run-make-fulldeps/rustdoc-themes/Makefile
Normal file
10
src/test/run-make-fulldeps/rustdoc-themes/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-include ../tools.mk
|
||||||
|
|
||||||
|
# Test that rustdoc will properly load in a theme file and display it in the theme selector.
|
||||||
|
|
||||||
|
OUTPUT_DIR := "$(TMPDIR)/rustdoc-themes"
|
||||||
|
|
||||||
|
all:
|
||||||
|
cp $(S)/src/librustdoc/html/static/themes/light.css $(TMPDIR)/test.css
|
||||||
|
$(RUSTDOC) -o $(OUTPUT_DIR) foo.rs --theme $(TMPDIR)/test.css
|
||||||
|
$(HTMLDOCCK) $(OUTPUT_DIR) foo.rs
|
4
src/test/run-make-fulldeps/rustdoc-themes/foo.rs
Normal file
4
src/test/run-make-fulldeps/rustdoc-themes/foo.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// @has test.css
|
||||||
|
// @has foo/struct.Foo.html
|
||||||
|
// @has - '//link[@rel="stylesheet"]/@href' '../test.css'
|
||||||
|
pub struct Foo;
|
|
@ -15,7 +15,7 @@ RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER)
|
||||||
RUSTDOC := $(RUSTDOC) -Clinker=$(RUSTC_LINKER)
|
RUSTDOC := $(RUSTDOC) -Clinker=$(RUSTC_LINKER)
|
||||||
endif
|
endif
|
||||||
#CC := $(CC) -L $(TMPDIR)
|
#CC := $(CC) -L $(TMPDIR)
|
||||||
HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py
|
HTMLDOCCK := '$(PYTHON)' '$(S)/src/etc/htmldocck.py'
|
||||||
CGREP := "$(S)/src/etc/cat-and-grep.sh"
|
CGREP := "$(S)/src/etc/cat-and-grep.sh"
|
||||||
|
|
||||||
# This is the name of the binary we will generate and run; use this
|
# This is the name of the binary we will generate and run; use this
|
||||||
|
|
|
@ -38,9 +38,11 @@ fn main() {
|
||||||
eprintln!("No theme found in \"{}\"...", themes_folder);
|
eprintln!("No theme found in \"{}\"...", themes_folder);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
let arg_name = "--check-theme".to_owned();
|
||||||
let status = Command::new(rustdoc_bin)
|
let status = Command::new(rustdoc_bin)
|
||||||
.args(&["-Z", "unstable-options", "--theme-checker"])
|
.args(&themes.iter()
|
||||||
.args(&themes)
|
.flat_map(|t| vec![&arg_name, t].into_iter())
|
||||||
|
.collect::<Vec<_>>())
|
||||||
.status()
|
.status()
|
||||||
.expect("failed to execute child");
|
.expect("failed to execute child");
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue