1
Fork 0

Auto merge of #32230 - GuillaumeGomez:extend_css, r=alexcrichton

Add --extend-css option to rustdoc

Fixes #32223

r? @brson
This commit is contained in:
bors 2016-04-06 17:11:44 -07:00
commit a9f34c86a4
5 changed files with 196 additions and 123 deletions

View file

@ -785,12 +785,12 @@ impl RustcOptGroup {
self.stability == OptionStability::Stable self.stability == OptionStability::Stable
} }
fn stable(g: getopts::OptGroup) -> RustcOptGroup { pub fn stable(g: getopts::OptGroup) -> RustcOptGroup {
RustcOptGroup { opt_group: g, stability: OptionStability::Stable } RustcOptGroup { opt_group: g, stability: OptionStability::Stable }
} }
#[allow(dead_code)] // currently we have no "truly unstable" options #[allow(dead_code)] // currently we have no "truly unstable" options
fn unstable(g: getopts::OptGroup) -> RustcOptGroup { pub fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
RustcOptGroup { opt_group: g, stability: OptionStability::Unstable } RustcOptGroup { opt_group: g, stability: OptionStability::Unstable }
} }
@ -926,33 +926,32 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
pub fn rustc_optgroups() -> Vec<RustcOptGroup> { pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
let mut opts = rustc_short_optgroups(); let mut opts = rustc_short_optgroups();
opts.extend_from_slice(&[ opts.extend_from_slice(&[
opt::multi_s("", "extern", "Specify where an external rust library is \ opt::multi_s("", "extern", "Specify where an external rust library is located",
located", "NAME=PATH"),
"NAME=PATH"),
opt::opt_s("", "sysroot", "Override the system root", "PATH"), opt::opt_s("", "sysroot", "Override the system root", "PATH"),
opt::multi_ubnr("Z", "", "Set internal debugging options", "FLAG"), opt::multi_ubnr("Z", "", "Set internal debugging options", "FLAG"),
opt::opt_ubnr("", "error-format", opt::opt_ubnr("", "error-format",
"How errors and other messages are produced", "How errors and other messages are produced",
"human|json"), "human|json"),
opt::opt_s("", "color", "Configure coloring of output: opt::opt_s("", "color", "Configure coloring of output:
auto = colorize, if output goes to a tty (default); auto = colorize, if output goes to a tty (default);
always = always colorize output; always = always colorize output;
never = never colorize output", "auto|always|never"), never = never colorize output", "auto|always|never"),
opt::flagopt_ubnr("", "pretty", opt::flagopt_ubnr("", "pretty",
"Pretty-print the input instead of compiling; "Pretty-print the input instead of compiling;
valid types are: `normal` (un-annotated source), valid types are: `normal` (un-annotated source),
`expanded` (crates expanded), or `expanded` (crates expanded), or
`expanded,identified` (fully parenthesized, AST nodes with IDs).", `expanded,identified` (fully parenthesized, AST nodes with IDs).",
"TYPE"), "TYPE"),
opt::flagopt_ubnr("", "unpretty", opt::flagopt_ubnr("", "unpretty",
"Present the input source, unstable (and less-pretty) variants; "Present the input source, unstable (and less-pretty) variants;
valid types are any of the types for `--pretty`, as well as: valid types are any of the types for `--pretty`, as well as:
`flowgraph=<nodeid>` (graphviz formatted flowgraph for node), `flowgraph=<nodeid>` (graphviz formatted flowgraph for node),
`everybody_loops` (all function bodies replaced with `loop {}`), `everybody_loops` (all function bodies replaced with `loop {}`),
`hir` (the HIR), `hir,identified`, or `hir` (the HIR), `hir,identified`, or
`hir,typed` (HIR with types for each node).", `hir,typed` (HIR with types for each node).",
"TYPE"), "TYPE"),
// new options here should **not** use the `_ubnr` functions, all new // new options here should **not** use the `_ubnr` functions, all new
// unstable options should use the short variants to indicate that they // unstable options should use the short variants to indicate that they
@ -1263,7 +1262,6 @@ pub fn get_unstable_features_setting() -> UnstableFeatures {
} }
pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> { pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
let mut crate_types: Vec<CrateType> = Vec::new(); let mut crate_types: Vec<CrateType> = Vec::new();
for unparsed_crate_type in &list_list { for unparsed_crate_type in &list_list {
for part in unparsed_crate_type.split(',') { for part in unparsed_crate_type.split(',') {
@ -1287,6 +1285,72 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
return Ok(crate_types); return Ok(crate_types);
} }
pub mod nightly_options {
use getopts;
use syntax::feature_gate::UnstableFeatures;
use super::{ErrorOutputType, OptionStability, RustcOptGroup, get_unstable_features_setting};
use session::{early_error, early_warn};
pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
}
fn is_nightly_build() -> bool {
match get_unstable_features_setting() {
UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
_ => false,
}
}
pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
let really_allows_unstable_options = match get_unstable_features_setting() {
UnstableFeatures::Disallow => false,
_ => true,
};
for opt in flags.iter() {
if opt.stability == OptionStability::Stable {
continue
}
let opt_name = if opt.opt_group.long_name.is_empty() {
&opt.opt_group.short_name
} else {
&opt.opt_group.long_name
};
if !matches.opt_present(opt_name) {
continue
}
if opt_name != "Z" && !has_z_unstable_option {
early_error(ErrorOutputType::default(),
&format!("the `-Z unstable-options` flag must also be passed to enable \
the flag `{}`",
opt_name));
}
if really_allows_unstable_options {
continue
}
match opt.stability {
OptionStability::Unstable => {
let msg = format!("the option `{}` is only accepted on the \
nightly compiler", opt_name);
early_error(ErrorOutputType::default(), &msg);
}
OptionStability::UnstableButNotReally => {
let msg = format!("the option `{}` is is unstable and should \
only be used on the nightly compiler, but \
it is currently accepted for backwards \
compatibility; this will soon change, \
see issue #31847 for more details",
opt_name);
early_warn(ErrorOutputType::default(), &msg);
}
OptionStability::Stable => {}
}
}
}
}
impl fmt::Display for CrateType { impl fmt::Display for CrateType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {

View file

@ -67,7 +67,7 @@ use rustc_save_analysis as save;
use rustc_trans::back::link; use rustc_trans::back::link;
use rustc::session::{config, Session, build_session, CompileResult}; use rustc::session::{config, Session, build_session, CompileResult};
use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType}; use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType};
use rustc::session::config::{get_unstable_features_setting, OptionStability}; use rustc::session::config::{get_unstable_features_setting, nightly_options};
use rustc::middle::cstore::CrateStore; use rustc::middle::cstore::CrateStore;
use rustc::lint::Lint; use rustc::lint::Lint;
use rustc::lint; use rustc::lint;
@ -88,7 +88,7 @@ use std::str;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use rustc::session::{early_error, early_warn}; use rustc::session::early_error;
use syntax::ast; use syntax::ast;
use syntax::parse::{self, PResult}; use syntax::parse::{self, PResult};
@ -909,51 +909,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// (unstable option being used on stable) // (unstable option being used on stable)
// * If we're a historically stable-but-should-be-unstable option then we // * If we're a historically stable-but-should-be-unstable option then we
// emit a warning that we're going to turn this into an error soon. // emit a warning that we're going to turn this into an error soon.
let has_z_unstable_options = matches.opt_strs("Z") nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
.iter()
.any(|x| *x == "unstable-options");
let really_allows_unstable_options = match get_unstable_features_setting() {
UnstableFeatures::Disallow => false,
_ => true,
};
for opt in config::rustc_optgroups() {
if opt.stability == OptionStability::Stable {
continue
}
let opt_name = if opt.opt_group.long_name.is_empty() {
&opt.opt_group.short_name
} else {
&opt.opt_group.long_name
};
if !matches.opt_present(opt_name) {
continue
}
if opt_name != "Z" && !has_z_unstable_options {
let msg = format!("the `-Z unstable-options` flag must also be \
passed to enable the flag `{}`", opt_name);
early_error(ErrorOutputType::default(), &msg);
}
if really_allows_unstable_options {
continue
}
match opt.stability {
OptionStability::Unstable => {
let msg = format!("the option `{}` is only accepted on the \
nightly compiler", opt_name);
early_error(ErrorOutputType::default(), &msg);
}
OptionStability::UnstableButNotReally => {
let msg = format!("the option `{}` is is unstable and should \
only be used on the nightly compiler, but \
it is currently accepted for backwards \
compatibility; this will soon change, \
see issue #31847 for more details",
opt_name);
early_warn(ErrorOutputType::default(), &msg);
}
OptionStability::Stable => {}
}
}
if matches.opt_present("h") || matches.opt_present("help") { if matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we *really* accept unstable // Only show unstable options in --help if we *really* accept unstable
@ -961,12 +917,11 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// the stable channel of Rust which was accidentally allowed // the stable channel of Rust which was accidentally allowed
// historically. // historically.
usage(matches.opt_present("verbose"), usage(matches.opt_present("verbose"),
has_z_unstable_options && really_allows_unstable_options); nightly_options::is_unstable_enabled(&matches));
return None; return None;
} }
// Don't handle -W help here, because we might first load plugins. // Don't handle -W help here, because we might first load plugins.
let r = matches.opt_strs("Z"); let r = matches.opt_strs("Z");
if r.iter().any(|x| *x == "help") { if r.iter().any(|x| *x == "help") {
describe_debug_flags(); describe_debug_flags();

View file

@ -28,11 +28,12 @@ pub struct Page<'a> {
pub ty: &'a str, pub ty: &'a str,
pub root_path: &'a str, pub root_path: &'a str,
pub description: &'a str, pub description: &'a str,
pub keywords: &'a str pub keywords: &'a str,
} }
pub fn render<T: fmt::Display, S: fmt::Display>( pub fn render<T: fmt::Display, S: fmt::Display>(
dst: &mut io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T) dst: &mut io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T,
css_file_extension: bool)
-> io::Result<()> -> io::Result<()>
{ {
write!(dst, write!(dst,
@ -49,6 +50,7 @@ r##"<!DOCTYPE html>
<link rel="stylesheet" type="text/css" href="{root_path}rustdoc.css"> <link rel="stylesheet" type="text/css" href="{root_path}rustdoc.css">
<link rel="stylesheet" type="text/css" href="{root_path}main.css"> <link rel="stylesheet" type="text/css" href="{root_path}main.css">
{css_extension}
{favicon} {favicon}
{in_header} {in_header}
@ -141,6 +143,12 @@ r##"<!DOCTYPE html>
<script defer src="{root_path}search-index.js"></script> <script defer src="{root_path}search-index.js"></script>
</body> </body>
</html>"##, </html>"##,
css_extension = if css_file_extension {
format!("<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}theme.css\">",
root_path = page.root_path)
} else {
"".to_owned()
},
content = *t, content = *t,
root_path = page.root_path, root_path = page.root_path,
ty = page.ty, ty = page.ty,

View file

@ -119,6 +119,9 @@ pub struct SharedContext {
/// The base-URL of the issue tracker for when an item has been tagged with /// The base-URL of the issue tracker for when an item has been tagged with
/// an issue number. /// an issue number.
pub issue_tracker_base_url: Option<String>, pub issue_tracker_base_url: Option<String>,
/// The given user css file which allow to customize the generated
/// documentation theme.
pub css_file_extension: Option<PathBuf>,
} }
/// Indicates where an external crate can be found. /// Indicates where an external crate can be found.
@ -411,7 +414,8 @@ pub fn derive_id(candidate: String) -> String {
pub fn run(mut krate: clean::Crate, pub fn run(mut krate: clean::Crate,
external_html: &ExternalHtml, external_html: &ExternalHtml,
dst: PathBuf, dst: PathBuf,
passes: HashSet<String>) -> Result<(), Error> { passes: HashSet<String>,
css_file_extension: Option<PathBuf>) -> Result<(), Error> {
let src_root = match krate.src.parent() { let src_root = match krate.src.parent() {
Some(p) => p.to_path_buf(), Some(p) => p.to_path_buf(),
None => PathBuf::new(), None => PathBuf::new(),
@ -429,6 +433,7 @@ pub fn run(mut krate: clean::Crate,
krate: krate.name.clone(), krate: krate.name.clone(),
playground_url: "".to_string(), playground_url: "".to_string(),
}, },
css_file_extension: css_file_extension.clone(),
}; };
// Crawl the crate attributes looking for attributes which control how we're // Crawl the crate attributes looking for attributes which control how we're
@ -637,6 +642,7 @@ fn write_shared(cx: &Context,
// Add all the static files. These may already exist, but we just // 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. // overwrite them anyway to make sure that they're fresh and up-to-date.
write(cx.dst.join("jquery.js"), write(cx.dst.join("jquery.js"),
include_bytes!("static/jquery-2.1.4.min.js"))?; include_bytes!("static/jquery-2.1.4.min.js"))?;
write(cx.dst.join("main.js"), write(cx.dst.join("main.js"),
@ -647,6 +653,17 @@ fn write_shared(cx: &Context,
include_bytes!("static/rustdoc.css"))?; include_bytes!("static/rustdoc.css"))?;
write(cx.dst.join("main.css"), write(cx.dst.join("main.css"),
include_bytes!("static/styles/main.css"))?; include_bytes!("static/styles/main.css"))?;
if let Some(ref css) = cx.shared.css_file_extension {
let mut content = String::new();
let css = css.as_path();
let mut f = try_err!(File::open(css), css);
try_err!(f.read_to_string(&mut content), css);
let css = cx.dst.join("theme.css");
let css = css.as_path();
let mut f = try_err!(File::create(css), css);
try_err!(write!(f, "{}", &content), css);
}
write(cx.dst.join("normalize.css"), write(cx.dst.join("normalize.css"),
include_bytes!("static/normalize.css"))?; include_bytes!("static/normalize.css"))?;
write(cx.dst.join("FiraSans-Regular.woff"), write(cx.dst.join("FiraSans-Regular.woff"),
@ -932,7 +949,8 @@ impl<'a> SourceCollector<'a> {
keywords: BASIC_KEYWORDS, keywords: BASIC_KEYWORDS,
}; };
layout::render(&mut w, &self.scx.layout, layout::render(&mut w, &self.scx.layout,
&page, &(""), &Source(contents))?; &page, &(""), &Source(contents),
self.scx.css_file_extension.is_some())?;
w.flush()?; w.flush()?;
self.scx.local_sources.insert(p, href); self.scx.local_sources.insert(p, href);
Ok(()) Ok(())
@ -1294,8 +1312,8 @@ impl Context {
if !cx.render_redirect_pages { if !cx.render_redirect_pages {
layout::render(&mut writer, &cx.shared.layout, &page, layout::render(&mut writer, &cx.shared.layout, &page,
&Sidebar{ cx: cx, item: it }, &Sidebar{ cx: cx, item: it },
&Item{ cx: cx, item: it })?; &Item{ cx: cx, item: it },
cx.shared.css_file_extension.is_some())?;
} else { } else {
let mut url = repeat("../").take(cx.current.len()) let mut url = repeat("../").take(cx.current.len())
.collect::<String>(); .collect::<String>();

View file

@ -65,7 +65,7 @@ use externalfiles::ExternalHtml;
use serialize::Decodable; use serialize::Decodable;
use serialize::json::{self, Json}; use serialize::json::{self, Json};
use rustc::session::search_paths::SearchPaths; use rustc::session::search_paths::SearchPaths;
use rustc::session::config::ErrorOutputType; use rustc::session::config::{ErrorOutputType, RustcOptGroup, nightly_options};
// reexported from `clean` so it can be easily updated with the mod itself // reexported from `clean` so it can be easily updated with the mod itself
pub use clean::SCHEMA_VERSION; pub use clean::SCHEMA_VERSION;
@ -140,68 +140,87 @@ pub fn main() {
process::exit(res as i32); process::exit(res as i32);
} }
pub fn opts() -> Vec<getopts::OptGroup> { fn stable(g: getopts::OptGroup) -> RustcOptGroup { RustcOptGroup::stable(g) }
fn unstable(g: getopts::OptGroup) -> RustcOptGroup { RustcOptGroup::unstable(g) }
pub fn opts() -> Vec<RustcOptGroup> {
use getopts::*; use getopts::*;
vec!( vec!(
optflag("h", "help", "show this help message"), stable(optflag("h", "help", "show this help message")),
optflag("V", "version", "print rustdoc's version"), stable(optflag("V", "version", "print rustdoc's version")),
optflag("v", "verbose", "use verbose output"), stable(optflag("v", "verbose", "use verbose output")),
optopt("r", "input-format", "the input type of the specified file", stable(optopt("r", "input-format", "the input type of the specified file",
"[rust|json]"), "[rust|json]")),
optopt("w", "output-format", "the output type to write", stable(optopt("w", "output-format", "the output type to write",
"[html|json]"), "[html|json]")),
optopt("o", "output", "where to place the output", "PATH"), stable(optopt("o", "output", "where to place the output", "PATH")),
optopt("", "crate-name", "specify the name of this crate", "NAME"), stable(optopt("", "crate-name", "specify the name of this crate", "NAME")),
optmulti("L", "library-path", "directory to add to crate search path", stable(optmulti("L", "library-path", "directory to add to crate search path",
"DIR"), "DIR")),
optmulti("", "cfg", "pass a --cfg to rustc", ""), stable(optmulti("", "cfg", "pass a --cfg to rustc", "")),
optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH"), stable(optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH")),
optmulti("", "plugin-path", "directory to load plugins from", "DIR"), stable(optmulti("", "plugin-path", "directory to load plugins from", "DIR")),
optmulti("", "passes", "list of passes to also run, you might want \ stable(optmulti("", "passes",
to pass it multiple times; a value of `list` \ "list of passes to also run, you might want \
will print available passes", to pass it multiple times; a value of `list` \
"PASSES"), will print available passes",
optmulti("", "plugins", "space separated list of plugins to also load", "PASSES")),
"PLUGINS"), stable(optmulti("", "plugins", "space separated list of plugins to also load",
optflag("", "no-defaults", "don't run the default passes"), "PLUGINS")),
optflag("", "test", "run code examples as tests"), stable(optflag("", "no-defaults", "don't run the default passes")),
optmulti("", "test-args", "arguments to pass to the test runner", stable(optflag("", "test", "run code examples as tests")),
"ARGS"), stable(optmulti("", "test-args", "arguments to pass to the test runner",
optopt("", "target", "target triple to document", "TRIPLE"), "ARGS")),
optmulti("", "markdown-css", "CSS files to include via <link> in a rendered Markdown file", stable(optopt("", "target", "target triple to document", "TRIPLE")),
"FILES"), stable(optmulti("", "markdown-css",
optmulti("", "html-in-header", "CSS files to include via <link> in a rendered Markdown file",
"files to include inline in the <head> section of a rendered Markdown file \ "FILES")),
or generated documentation", stable(optmulti("", "html-in-header",
"FILES"), "files to include inline in the <head> section of a rendered Markdown file \
optmulti("", "html-before-content", or generated documentation",
"files to include inline between <body> and the content of a rendered \ "FILES")),
Markdown file or generated documentation", stable(optmulti("", "html-before-content",
"FILES"), "files to include inline between <body> and the content of a rendered \
optmulti("", "html-after-content", Markdown file or generated documentation",
"files to include inline between the content and </body> of a rendered \ "FILES")),
Markdown file or generated documentation", stable(optmulti("", "html-after-content",
"FILES"), "files to include inline between the content and </body> of a rendered \
optopt("", "markdown-playground-url", Markdown file or generated documentation",
"URL to send code snippets to", "URL"), "FILES")),
optflag("", "markdown-no-toc", "don't include table of contents") stable(optopt("", "markdown-playground-url",
"URL to send code snippets to", "URL")),
stable(optflag("", "markdown-no-toc", "don't include table of contents")),
unstable(optopt("e", "extend-css",
"to redefine some css rules with a given file to generate doc with your \
own theme", "PATH")),
unstable(optmulti("Z", "",
"internal and debugging options (only on nightly build)", "FLAG")),
) )
} }
pub fn usage(argv0: &str) { pub fn usage(argv0: &str) {
println!("{}", println!("{}",
getopts::usage(&format!("{} [options] <input>", argv0), getopts::usage(&format!("{} [options] <input>", argv0),
&opts())); &opts().into_iter()
.map(|x| x.opt_group)
.collect::<Vec<getopts::OptGroup>>()));
} }
pub fn main_args(args: &[String]) -> isize { pub fn main_args(args: &[String]) -> isize {
let matches = match getopts::getopts(&args[1..], &opts()) { let all_groups: Vec<getopts::OptGroup> = opts()
.into_iter()
.map(|x| x.opt_group)
.collect();
let matches = match getopts::getopts(&args[1..], &all_groups) {
Ok(m) => m, Ok(m) => m,
Err(err) => { Err(err) => {
println!("{}", err); println!("{}", err);
return 1; return 1;
} }
}; };
// Check for unstable options.
nightly_options::check_nightly_options(&matches, &opts());
if matches.opt_present("h") || matches.opt_present("help") { if matches.opt_present("h") || matches.opt_present("help") {
usage(&args[0]); usage(&args[0]);
return 0; return 0;
@ -253,8 +272,16 @@ pub fn main_args(args: &[String]) -> isize {
let markdown_input = input.ends_with(".md") || input.ends_with(".markdown"); let markdown_input = input.ends_with(".md") || input.ends_with(".markdown");
let output = matches.opt_str("o").map(|s| PathBuf::from(&s)); let output = matches.opt_str("o").map(|s| PathBuf::from(&s));
let css_file_extension = matches.opt_str("e").map(|s| PathBuf::from(&s));
let cfgs = matches.opt_strs("cfg"); let cfgs = matches.opt_strs("cfg");
if let Some(ref p) = css_file_extension {
if !p.is_file() {
println!("{}", "--extend-css option must take a css file as input");
return 1;
}
}
let external_html = match ExternalHtml::load( let external_html = match ExternalHtml::load(
&matches.opt_strs("html-in-header"), &matches.opt_strs("html-in-header"),
&matches.opt_strs("html-before-content"), &matches.opt_strs("html-before-content"),
@ -290,7 +317,8 @@ pub fn main_args(args: &[String]) -> isize {
Some("html") | None => { Some("html") | None => {
html::render::run(krate, &external_html, html::render::run(krate, &external_html,
output.unwrap_or(PathBuf::from("doc")), output.unwrap_or(PathBuf::from("doc")),
passes.into_iter().collect()) passes.into_iter().collect(),
css_file_extension)
.expect("failed to generate documentation") .expect("failed to generate documentation")
} }
Some("json") => { Some("json") => {