1
Fork 0

auto merge of #15237 : zzmp/rust/feat/markdown-in-crate-documentation, r=huonw

This makes the `in-header`, `markdown-before-content`, and `markdown-after-content` options available to `rustdoc` when generating documentation for any crate.

Before, these options were only available when creating documentation *from* markdown. Now, they are available when generating documentation from source.

This also updates the `rustdoc -h` output to reflect these changes. It does not update the `man rustdoc` page, nor does it update the documentation in [the `rustdoc` manual](http://doc.rust-lang.org/rustdoc.html).
This commit is contained in:
bors 2014-06-30 07:31:29 +00:00
commit 2735c7bb10
8 changed files with 143 additions and 69 deletions

View file

@ -38,6 +38,15 @@ directory to load plugins from (default: /tmp/rustdoc_ng/plugins)
-L --library-path <val> -L --library-path <val>
directory to add to crate search path directory to add to crate search path
.TP .TP
--html-in-header <val>
file to add to <head>
.TP
--html-before-content <val>
file to add in <body>, before content
.TP
--html-after-content <val>
file to add in <body>, after content
.TP
-h, --help -h, --help
Print help Print help

View file

@ -35,16 +35,16 @@ DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \
PDF_DOCS := tutorial rust PDF_DOCS := tutorial rust
RUSTDOC_DEPS_rust := doc/full-toc.inc RUSTDOC_DEPS_rust := doc/full-toc.inc
RUSTDOC_FLAGS_rust := --markdown-in-header=doc/full-toc.inc RUSTDOC_FLAGS_rust := --html-in-header=doc/full-toc.inc
L10N_LANGS := ja L10N_LANGS := ja
# Generally no need to edit below here. # Generally no need to edit below here.
# The options are passed to the documentation generators. # The options are passed to the documentation generators.
RUSTDOC_HTML_OPTS_NO_CSS = --markdown-before-content=doc/version_info.html \ RUSTDOC_HTML_OPTS_NO_CSS = --html-before-content=doc/version_info.html \
--markdown-in-header=doc/favicon.inc \ --html-in-header=doc/favicon.inc \
--markdown-after-content=doc/footer.inc \ --html-after-content=doc/footer.inc \
--markdown-playground-url='http://play.rust-lang.org/' --markdown-playground-url='http://play.rust-lang.org/'
RUSTDOC_HTML_OPTS = $(RUSTDOC_HTML_OPTS_NO_CSS) --markdown-css rust.css RUSTDOC_HTML_OPTS = $(RUSTDOC_HTML_OPTS_NO_CSS) --markdown-css rust.css

View file

@ -103,6 +103,17 @@ rustdoc can also generate JSON, for consumption by other tools, with
`rustdoc --output-format json`, and also consume already-generated JSON with `rustdoc --output-format json`, and also consume already-generated JSON with
`rustdoc --input-format json`. `rustdoc --input-format json`.
rustdoc also supports personalizing the output from crates' documentation,
similar to markdown options.
- `--html-in-header FILE`: includes the contents of `FILE` at the
end of the `<head>...</head>` section.
- `--html-before-content FILE`: includes the contents of `FILE`
directly after `<body>`, before the rendered content (including the
search bar).
- `--html-after-content FILE`: includes the contents of `FILE`
after all the rendered content.
# Using the Documentation # Using the Documentation
The web pages generated by rustdoc present the same logical hierarchy that one The web pages generated by rustdoc present the same logical hierarchy that one
@ -238,16 +249,16 @@ detected by a `.md` or `.markdown` extension.
There are 4 options to modify the output that Rustdoc creates. There are 4 options to modify the output that Rustdoc creates.
- `--markdown-css PATH`: adds a `<link rel="stylesheet">` tag pointing to `PATH`. - `--markdown-css PATH`: adds a `<link rel="stylesheet">` tag pointing to `PATH`.
- `--markdown-in-header FILE`: includes the contents of `FILE` at the - `--html-in-header FILE`: includes the contents of `FILE` at the
end of the `<head>...</head>` section. end of the `<head>...</head>` section.
- `--markdown-before-content FILE`: includes the contents of `FILE` - `--html-before-content FILE`: includes the contents of `FILE`
directly after `<body>`, before the rendered content (including the directly after `<body>`, before the rendered content (including the
title). title).
- `--markdown-after-content FILE`: includes the contents of `FILE` - `--html-after-content FILE`: includes the contents of `FILE`
directly before `</body>`, after all the rendered content. directly before `</body>`, after all the rendered content.
All of these can be specified multiple times, and they are output in All of these can be specified multiple times, and they are output in
the order in which they are specified. The first line of the file must the order in which they are specified. The first line of the file being rendered must
be the title, prefixed with `%` (e.g. this page has `% Rust be the title, prefixed with `%` (e.g. this page has `% Rust
Documentation` on the first line). Documentation` on the first line).

View file

@ -0,0 +1,70 @@
// Copyright 2014 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.
use std::{io, str};
#[deriving(Clone)]
pub struct ExternalHtml{
pub in_header: String,
pub before_content: String,
pub after_content: String
}
impl ExternalHtml {
pub fn load(in_header: &[String], before_content: &[String], after_content: &[String])
-> Option<ExternalHtml> {
match (load_external_files(in_header),
load_external_files(before_content),
load_external_files(after_content)) {
(Some(ih), Some(bc), Some(ac)) => Some(ExternalHtml {
in_header: ih,
before_content: bc,
after_content: ac
}),
_ => None
}
}
}
pub fn load_string(input: &Path) -> io::IoResult<Option<String>> {
let mut f = try!(io::File::open(input));
let d = try!(f.read_to_end());
Ok(str::from_utf8(d.as_slice()).map(|s| s.to_string()))
}
macro_rules! load_or_return {
($input: expr, $cant_read: expr, $not_utf8: expr) => {
{
let input = Path::new($input);
match ::externalfiles::load_string(&input) {
Err(e) => {
let _ = writeln!(&mut io::stderr(),
"error reading `{}`: {}", input.display(), e);
return $cant_read;
}
Ok(None) => {
let _ = writeln!(&mut io::stderr(),
"error reading `{}`: not UTF-8", input.display());
return $not_utf8;
}
Ok(Some(s)) => s
}
}
}
}
pub fn load_external_files(names: &[String]) -> Option<String> {
let mut out = String::new();
for name in names.iter() {
out.push_str(load_or_return!(name.as_slice(), None, None).as_slice());
out.push_char('\n');
}
Some(out)
}

View file

@ -11,10 +11,13 @@
use std::fmt; use std::fmt;
use std::io; use std::io;
use externalfiles::ExternalHtml;
#[deriving(Clone)] #[deriving(Clone)]
pub struct Layout { pub struct Layout {
pub logo: String, pub logo: String,
pub favicon: String, pub favicon: String,
pub external_html: ExternalHtml,
pub krate: String, pub krate: String,
pub playground_url: String, pub playground_url: String,
} }
@ -44,6 +47,7 @@ r##"<!DOCTYPE html>
<link rel="stylesheet" type="text/css" href="{root_path}main.css"> <link rel="stylesheet" type="text/css" href="{root_path}main.css">
{favicon} {favicon}
{in_header}
</head> </head>
<body> <body>
<!--[if lte IE 8]> <!--[if lte IE 8]>
@ -53,6 +57,8 @@ r##"<!DOCTYPE html>
</div> </div>
<![endif]--> <![endif]-->
{before_content}
<section class="sidebar"> <section class="sidebar">
{logo} {logo}
{sidebar} {sidebar}
@ -105,6 +111,8 @@ r##"<!DOCTYPE html>
</div> </div>
</div> </div>
{after_content}
<script> <script>
window.rootPath = "{root_path}"; window.rootPath = "{root_path}";
window.currentCrate = "{krate}"; window.currentCrate = "{krate}";
@ -133,6 +141,9 @@ r##"<!DOCTYPE html>
} else { } else {
format!(r#"<link rel="shortcut icon" href="{}">"#, layout.favicon) format!(r#"<link rel="shortcut icon" href="{}">"#, layout.favicon)
}, },
in_header = layout.external_html.in_header,
before_content = layout.external_html.before_content,
after_content = layout.external_html.after_content,
sidebar = *sidebar, sidebar = *sidebar,
krate = layout.krate, krate = layout.krate,
play_url = layout.playground_url, play_url = layout.playground_url,

View file

@ -41,6 +41,8 @@ use std::str;
use std::string::String; use std::string::String;
use std::sync::Arc; use std::sync::Arc;
use externalfiles::ExternalHtml;
use serialize::json::ToJson; use serialize::json::ToJson;
use syntax::ast; use syntax::ast;
use syntax::ast_util; use syntax::ast_util;
@ -78,7 +80,7 @@ pub struct Context {
/// This changes as the context descends into the module hierarchy. /// This changes as the context descends into the module hierarchy.
pub dst: Path, pub dst: Path,
/// This describes the layout of each page, and is not modified after /// This describes the layout of each page, and is not modified after
/// creation of the context (contains info like the favicon) /// creation of the context (contains info like the favicon and added html).
pub layout: layout::Layout, pub layout: layout::Layout,
/// This map is a list of what should be displayed on the sidebar of the /// This map is a list of what should be displayed on the sidebar of the
/// current page. The key is the section header (traits, modules, /// current page. The key is the section header (traits, modules,
@ -220,7 +222,7 @@ local_data_key!(pub cache_key: Arc<Cache>)
local_data_key!(pub current_location_key: Vec<String> ) local_data_key!(pub current_location_key: Vec<String> )
/// Generates the documentation for `crate` into the directory `dst` /// Generates the documentation for `crate` into the directory `dst`
pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { pub fn run(mut krate: clean::Crate, external_html: &ExternalHtml, dst: Path) -> io::IoResult<()> {
let mut cx = Context { let mut cx = Context {
dst: dst, dst: dst,
current: Vec::new(), current: Vec::new(),
@ -229,12 +231,14 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
layout: layout::Layout { layout: layout::Layout {
logo: "".to_string(), logo: "".to_string(),
favicon: "".to_string(), favicon: "".to_string(),
external_html: external_html.clone(),
krate: krate.name.clone(), krate: krate.name.clone(),
playground_url: "".to_string(), playground_url: "".to_string(),
}, },
include_sources: true, include_sources: true,
render_redirect_pages: false, render_redirect_pages: false,
}; };
try!(mkdir(&cx.dst)); try!(mkdir(&cx.dst));
// Crawl the crate attributes looking for attributes which control how we're // Crawl the crate attributes looking for attributes which control how we're

View file

@ -32,6 +32,7 @@ use std::io::{File, MemWriter};
use std::str; use std::str;
use std::gc::Gc; use std::gc::Gc;
use serialize::{json, Decodable, Encodable}; use serialize::{json, Decodable, Encodable};
use externalfiles::ExternalHtml;
// 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;
@ -39,6 +40,8 @@ pub use clean::SCHEMA_VERSION;
pub mod clean; pub mod clean;
pub mod core; pub mod core;
pub mod doctree; pub mod doctree;
#[macro_escape]
pub mod externalfiles;
pub mod fold; pub mod fold;
pub mod html { pub mod html {
pub mod highlight; pub mod highlight;
@ -113,16 +116,17 @@ pub fn opts() -> Vec<getopts::OptGroup> {
"ARGS"), "ARGS"),
optmulti("", "markdown-css", "CSS files to include via <link> in a rendered Markdown file", optmulti("", "markdown-css", "CSS files to include via <link> in a rendered Markdown file",
"FILES"), "FILES"),
optmulti("", "markdown-in-header", optmulti("", "html-in-header",
"files to include inline in the <head> section of a rendered Markdown file", "files to include inline in the <head> section of a rendered Markdown file \
or generated documentation",
"FILES"), "FILES"),
optmulti("", "markdown-before-content", optmulti("", "html-before-content",
"files to include inline between <body> and the content of a rendered \ "files to include inline between <body> and the content of a rendered \
Markdown file", Markdown file or generated documentation",
"FILES"), "FILES"),
optmulti("", "markdown-after-content", optmulti("", "html-after-content",
"files to include inline between the content and </body> of a rendered \ "files to include inline between the content and </body> of a rendered \
Markdown file", Markdown file or generated documentation",
"FILES"), "FILES"),
optopt("", "markdown-playground-url", optopt("", "markdown-playground-url",
"URL to send code snippets to", "URL") "URL to send code snippets to", "URL")
@ -179,6 +183,14 @@ pub fn main_args(args: &[String]) -> int {
let output = matches.opt_str("o").map(|s| Path::new(s)); let output = matches.opt_str("o").map(|s| Path::new(s));
let cfgs = matches.opt_strs("cfg"); let cfgs = matches.opt_strs("cfg");
let external_html = match ExternalHtml::load(
matches.opt_strs("html-in-header").as_slice(),
matches.opt_strs("html-before-content").as_slice(),
matches.opt_strs("html-after-content").as_slice()) {
Some(eh) => eh,
None => return 3
};
match (should_test, markdown_input) { match (should_test, markdown_input) {
(true, true) => { (true, true) => {
return markdown::test(input, libs, test_args) return markdown::test(input, libs, test_args)
@ -187,7 +199,7 @@ pub fn main_args(args: &[String]) -> int {
return test::run(input, cfgs, libs, test_args) return test::run(input, cfgs, libs, test_args)
} }
(false, true) => return markdown::render(input, output.unwrap_or(Path::new("doc")), (false, true) => return markdown::render(input, output.unwrap_or(Path::new("doc")),
&matches), &matches, &external_html),
(false, false) => {} (false, false) => {}
} }
@ -215,7 +227,7 @@ pub fn main_args(args: &[String]) -> int {
let started = time::precise_time_ns(); let started = time::precise_time_ns();
match matches.opt_str("w").as_ref().map(|s| s.as_slice()) { match matches.opt_str("w").as_ref().map(|s| s.as_slice()) {
Some("html") | None => { Some("html") | None => {
match html::render::run(krate, output.unwrap_or(Path::new("doc"))) { match html::render::run(krate, &external_html, output.unwrap_or(Path::new("doc"))) {
Ok(()) => {} Ok(()) => {}
Err(e) => fail!("failed to generate documentation: {}", e), Err(e) => fail!("failed to generate documentation: {}", e),
} }

View file

@ -9,43 +9,19 @@
// except according to those terms. // except according to those terms.
use std::collections::HashSet; use std::collections::HashSet;
use std::{str, io}; use std::io;
use std::string::String; use std::string::String;
use getopts; use getopts;
use testing; use testing;
use externalfiles::ExternalHtml;
use html::escape::Escape; use html::escape::Escape;
use html::markdown; use html::markdown;
use html::markdown::{MarkdownWithToc, find_testable_code, reset_headers}; use html::markdown::{MarkdownWithToc, find_testable_code, reset_headers};
use test::Collector; use test::Collector;
fn load_string(input: &Path) -> io::IoResult<Option<String>> {
let mut f = try!(io::File::open(input));
let d = try!(f.read_to_end());
Ok(str::from_utf8(d.as_slice()).map(|s| s.to_string()))
}
macro_rules! load_or_return {
($input: expr, $cant_read: expr, $not_utf8: expr) => {
{
let input = Path::new($input);
match load_string(&input) {
Err(e) => {
let _ = writeln!(&mut io::stderr(),
"error reading `{}`: {}", input.display(), e);
return $cant_read;
}
Ok(None) => {
let _ = writeln!(&mut io::stderr(),
"error reading `{}`: not UTF-8", input.display());
return $not_utf8;
}
Ok(Some(s)) => s
}
}
}
}
/// Separate any lines at the start of the file that begin with `%`. /// Separate any lines at the start of the file that begin with `%`.
fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) { fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) {
let mut metadata = Vec::new(); let mut metadata = Vec::new();
@ -62,18 +38,10 @@ fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) {
(metadata, "") (metadata, "")
} }
fn load_external_files(names: &[String]) -> Option<String> {
let mut out = String::new();
for name in names.iter() {
out.push_str(load_or_return!(name.as_slice(), None, None).as_slice());
out.push_char('\n');
}
Some(out)
}
/// Render `input` (e.g. "foo.md") into an HTML file in `output` /// Render `input` (e.g. "foo.md") into an HTML file in `output`
/// (e.g. output = "bar" => "bar/foo.html"). /// (e.g. output = "bar" => "bar/foo.html").
pub fn render(input: &str, mut output: Path, matches: &getopts::Matches) -> int { pub fn render(input: &str, mut output: Path, matches: &getopts::Matches,
external_html: &ExternalHtml) -> int {
let input_p = Path::new(input); let input_p = Path::new(input);
output.push(input_p.filestem().unwrap()); output.push(input_p.filestem().unwrap());
output.set_extension("html"); output.set_extension("html");
@ -91,17 +59,6 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches) -> int
} }
let playground = playground.unwrap_or("".to_string()); let playground = playground.unwrap_or("".to_string());
let (in_header, before_content, after_content) =
match (load_external_files(matches.opt_strs("markdown-in-header")
.as_slice()),
load_external_files(matches.opt_strs("markdown-before-content")
.as_slice()),
load_external_files(matches.opt_strs("markdown-after-content")
.as_slice())) {
(Some(a), Some(b), Some(c)) => (a,b,c),
_ => return 3
};
let mut out = match io::File::create(&output) { let mut out = match io::File::create(&output) {
Err(e) => { Err(e) => {
let _ = writeln!(&mut io::stderr(), let _ = writeln!(&mut io::stderr(),
@ -153,10 +110,10 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches) -> int
</html>"#, </html>"#,
title = Escape(title), title = Escape(title),
css = css, css = css,
in_header = in_header, in_header = external_html.in_header,
before_content = before_content, before_content = external_html.before_content,
text = MarkdownWithToc(text), text = MarkdownWithToc(text),
after_content = after_content, after_content = external_html.after_content,
playground = playground, playground = playground,
); );