mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-28 12:41:16 -05:00
Compare commits
42 Commits
update-rea
...
v0.2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cbc41d413 | ||
|
|
25c1ca1275 | ||
|
|
acbb951240 | ||
|
|
9e96165d8f | ||
|
|
5c5ef2f86b | ||
|
|
23ac06e2eb | ||
|
|
2ddbb37f49 | ||
|
|
a481735fa2 | ||
|
|
954cfa86e5 | ||
|
|
b675b91980 | ||
|
|
eb19d2d654 | ||
|
|
1052ee92e1 | ||
|
|
3598e905aa | ||
|
|
3f002979c4 | ||
|
|
742dbbc917 | ||
|
|
991a725c26 | ||
|
|
005dfc55bf | ||
|
|
8c86031384 | ||
|
|
42b87e0fbc | ||
|
|
33add4b532 | ||
|
|
b0513ee771 | ||
|
|
b4538da9c3 | ||
|
|
7ac3e50b37 | ||
|
|
13a9aab2b2 | ||
|
|
eccec9bb52 | ||
|
|
e63f53fe47 | ||
|
|
2c20c99d4a | ||
|
|
c6125b184f | ||
|
|
dfb6e3cb10 | ||
|
|
41071a5dd9 | ||
|
|
f6a7432569 | ||
|
|
89ea60e7a5 | ||
|
|
10b69e60c8 | ||
|
|
336e08fe50 | ||
|
|
5bfdf9fcc8 | ||
|
|
d2565af000 | ||
|
|
599e47f1f1 | ||
|
|
0c31ab2953 | ||
|
|
d729a762fe | ||
|
|
43b3d157d9 | ||
|
|
a9f3be6f44 | ||
|
|
34356b87a0 |
@@ -62,7 +62,7 @@ The quick guide is
|
||||
|
||||
1. Install it
|
||||
```
|
||||
rustup component add rustfmt-preview
|
||||
rustup component add rustfmt
|
||||
```
|
||||
1. You can now run `rustfmt` on a single file simply by...
|
||||
```
|
||||
@@ -87,14 +87,11 @@ The best documentation can be found over at [rust-clippy](https://github.com/rus
|
||||
|
||||
1. To install
|
||||
```
|
||||
rustup update
|
||||
rustup install nightly
|
||||
rustup component add clippy-preview --toolchain=nightly
|
||||
rustup component add clippy
|
||||
```
|
||||
2. Running clippy
|
||||
As you may notice from the previous step, Clippy is on the nightly branch, so running it is like
|
||||
```
|
||||
cargo +nightly clippy
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
Clippy has an ever growing list of checks, that are managed in [lint files](https://rust-lang-nursery.github.io/rust-clippy/master/index.html).
|
||||
|
||||
666
Cargo.lock
generated
666
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mdbook"
|
||||
version = "0.2.2-alpha.0"
|
||||
version = "0.2.3"
|
||||
authors = [
|
||||
"Mathieu David <mathieudavid@mathieudavid.org>",
|
||||
"Michael-F-Bryan <michaelfbryan@gmail.com>",
|
||||
@@ -26,7 +26,7 @@ pulldown-cmark = "0.1.2"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.5"
|
||||
toml = "0.4"
|
||||
toml = "0.4.8"
|
||||
memchr = "2.0"
|
||||
open = "1.1"
|
||||
regex = "1.0.0"
|
||||
|
||||
@@ -18,7 +18,7 @@ mdBook can also be installed from source
|
||||
|
||||
mdBook is written in **[Rust](https://www.rust-lang.org/)** and therefore needs
|
||||
to be compiled with **Cargo**. If you haven't already installed Rust, please go
|
||||
ahead and [install it](https://www.rust-lang.org/downloads.html) now.
|
||||
ahead and [install it](https://www.rust-lang.org/tools/install) now.
|
||||
|
||||
### Install Crates.io version
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@ your default web browser after building it.
|
||||
#### --dest-dir
|
||||
|
||||
The `--dest-dir` (`-d`) option allows you to change the output directory for the
|
||||
book. If not specified it will default to the value of the `build.build-dir` key
|
||||
in `book.toml`, or to `./book` relative to the book's root directory.
|
||||
book. Relative paths are interpreted relative to the book's root directory. If
|
||||
not specified it will default to the value of the `build.build-dir` key in
|
||||
`book.toml`, or to `./book`.
|
||||
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ mdbook clean path/to/book
|
||||
#### --dest-dir
|
||||
|
||||
The `--dest-dir` (`-d`) option allows you to override the book's output
|
||||
directory, which will be deleted by this command. If not specified it will
|
||||
default to the value of the `build.build-dir` key in `book.toml`, or to `./book`
|
||||
relative to the book's root directory.
|
||||
directory, which will be deleted by this command. Relative paths are interpreted
|
||||
relative to the book's root directory. If not specified it will default to the
|
||||
value of the `build.build-dir` key in `book.toml`, or to `./book`.
|
||||
|
||||
```bash
|
||||
mdbook clean --dest-dir=path/to/book
|
||||
|
||||
@@ -5,6 +5,9 @@ The serve command is used to preview a book by serving it over HTTP at
|
||||
changes, rebuilding the book and refreshing clients for each change. A websocket
|
||||
connection is used to trigger the client-side refresh.
|
||||
|
||||
***Note:*** *The `serve` command is for testing a book's HTML output, and is not
|
||||
intended to be a complete HTTP server for a website.*
|
||||
|
||||
#### Specify a directory
|
||||
|
||||
The `serve` command can take a directory as an argument to use as the book's
|
||||
@@ -40,10 +43,6 @@ default web browser after starting the server.
|
||||
#### --dest-dir
|
||||
|
||||
The `--dest-dir` (`-d`) option allows you to change the output directory for the
|
||||
book. If not specified it will default to the value of the `build.build-dir` key
|
||||
in `book.toml`, or to `./book` relative to the book's root directory.
|
||||
|
||||
-----
|
||||
|
||||
***Note:*** *The `serve` command is for testing, and is not intended to be a
|
||||
complete HTTP server for a website.*
|
||||
book. Relative paths are interpreted relative to the book's root directory. If
|
||||
not specified it will default to the value of the `build.build-dir` key in
|
||||
`book.toml`, or to `./book`.
|
||||
|
||||
@@ -48,5 +48,6 @@ comma-delimited list (`-L foo,bar`).
|
||||
#### --dest-dir
|
||||
|
||||
The `--dest-dir` (`-d`) option allows you to change the output directory for the
|
||||
book. If not specified it will default to the value of the `build.build-dir` key
|
||||
in `book.toml`, or to `./book` relative to the book's root directory.
|
||||
book. Relative paths are interpreted relative to the book's root directory. If
|
||||
not specified it will default to the value of the `build.build-dir` key in
|
||||
`book.toml`, or to `./book`.
|
||||
|
||||
@@ -22,5 +22,6 @@ your default web browser.
|
||||
#### --dest-dir
|
||||
|
||||
The `--dest-dir` (`-d`) option allows you to change the output directory for the
|
||||
book. If not specified it will default to the value of the `build.build-dir` key
|
||||
in `book.toml`, or to `./book` relative to the book's root directory.
|
||||
book. Relative paths are interpreted relative to the book's root directory. If
|
||||
not specified it will default to the value of the `build.build-dir` key in
|
||||
`book.toml`, or to `./book`.
|
||||
|
||||
@@ -146,6 +146,8 @@ The following configuration options are available:
|
||||
- **theme:** mdBook comes with a default theme and all the resource files needed
|
||||
for it. But if this option is set, mdBook will selectively overwrite the theme
|
||||
files with the ones found in the specified folder.
|
||||
- **default-theme:** The theme color scheme to select by default in the
|
||||
'Change Theme' dropdown. Defaults to `light`.
|
||||
- **curly-quotes:** Convert straight quotes to curly quotes, except for those
|
||||
that occur in code blocks and code spans. Defaults to `false`.
|
||||
- **google-analytics:** If you use Google Analytics, this option lets you enable
|
||||
@@ -163,6 +165,10 @@ The following configuration options are available:
|
||||
- **playpen:** A subtable for configuring various playpen settings.
|
||||
- **search:** A subtable for configuring the in-browser search functionality.
|
||||
mdBook must be compiled with the `search` feature enabled (on by default).
|
||||
- **git_repository_url:** A url to the git repository for the book. If provided
|
||||
an icon link will be output in the menu bar of the book.
|
||||
- **git_repository_icon:** The FontAwesome icon class to use for the git
|
||||
repository link. Defaults to `fa-github`.
|
||||
|
||||
Available configuration options for the `[output.html.playpen]` table:
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ fn main() {
|
||||
panic!("This example is intended to be part of a library");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Deemphasize;
|
||||
|
||||
impl Preprocessor for Deemphasize {
|
||||
@@ -56,10 +57,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_emphasis(
|
||||
num_removed_items: &mut usize,
|
||||
chapter: &mut Chapter,
|
||||
) -> Result<String> {
|
||||
fn remove_emphasis(num_removed_items: &mut usize, chapter: &mut Chapter) -> Result<String> {
|
||||
let mut buf = String::with_capacity(chapter.content.len());
|
||||
|
||||
let events = Parser::new(&chapter.content).filter(|e| {
|
||||
@@ -76,7 +74,7 @@ fn remove_emphasis(
|
||||
should_keep
|
||||
});
|
||||
|
||||
cmark(events, &mut buf, None).map(|_| buf).map_err(|err| {
|
||||
Error::from(format!("Markdown serialization failed: {}", err))
|
||||
})
|
||||
cmark(events, &mut buf, None)
|
||||
.map(|_| buf)
|
||||
.map_err(|err| Error::from(format!("Markdown serialization failed: {}", err)))
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use mdbook::book::Book;
|
||||
use mdbook::errors::Error;
|
||||
use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
|
||||
use nop_lib::Nop;
|
||||
use std::io;
|
||||
use std::process;
|
||||
use nop_lib::Nop;
|
||||
|
||||
pub fn make_app() -> App<'static, 'static> {
|
||||
App::new("nop-preprocessor")
|
||||
@@ -16,7 +16,8 @@ pub fn make_app() -> App<'static, 'static> {
|
||||
.subcommand(
|
||||
SubCommand::with_name("supports")
|
||||
.arg(Arg::with_name("renderer").required(true))
|
||||
.about("Check whether a renderer is supported by this preprocessor"))
|
||||
.about("Check whether a renderer is supported by this preprocessor"),
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -87,11 +88,7 @@ mod nop_lib {
|
||||
"nop-preprocessor"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
ctx: &PreprocessorContext,
|
||||
book: Book,
|
||||
) -> Result<Book, Error> {
|
||||
fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book, Error> {
|
||||
// In testing we want to tell the preprocessor to blow up by setting a
|
||||
// particular config value
|
||||
if let Some(nop_cfg) = ctx.config.get_preprocessor(self.name()) {
|
||||
@@ -109,4 +106,3 @@ mod nop_lib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,9 +167,9 @@ impl Chapter {
|
||||
) -> Chapter {
|
||||
Chapter {
|
||||
name: name.to_string(),
|
||||
content: content,
|
||||
content,
|
||||
path: path.into(),
|
||||
parent_names: parent_names,
|
||||
parent_names,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ fn load_summary_item<P: AsRef<Path>>(
|
||||
match *item {
|
||||
SummaryItem::Separator => Ok(BookItem::Separator),
|
||||
SummaryItem::Link(ref link) => {
|
||||
load_chapter(link, src_dir, parent_names).map(|c| BookItem::Chapter(c))
|
||||
load_chapter(link, src_dir, parent_names).map(BookItem::Chapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ impl BookBuilder {
|
||||
let summary = src_dir.join("SUMMARY.md");
|
||||
let mut f = File::create(&summary).chain_err(|| "Unable to create SUMMARY.md")?;
|
||||
writeln!(f, "# Summary")?;
|
||||
writeln!(f, "")?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "- [Chapter 1](./chapter_1.md)")?;
|
||||
|
||||
let chapter_1 = src_dir.join("chapter_1.md");
|
||||
|
||||
@@ -20,8 +20,9 @@ use tempfile::Builder as TempFileBuilder;
|
||||
use toml::Value;
|
||||
|
||||
use errors::*;
|
||||
use preprocess::{IndexPreprocessor, LinkPreprocessor, Preprocessor,
|
||||
PreprocessorContext, CmdPreprocessor};
|
||||
use preprocess::{
|
||||
CmdPreprocessor, IndexPreprocessor, LinkPreprocessor, Preprocessor, PreprocessorContext,
|
||||
};
|
||||
use renderer::{CmdRenderer, HtmlHandlebars, RenderContext, Renderer};
|
||||
use utils;
|
||||
|
||||
@@ -160,15 +161,16 @@ impl MDBook {
|
||||
/// Run the entire build process for a particular `Renderer`.
|
||||
fn execute_build_process(&self, renderer: &Renderer) -> Result<()> {
|
||||
let mut preprocessed_book = self.book.clone();
|
||||
let preprocess_ctx = PreprocessorContext::new(self.root.clone(),
|
||||
self.config.clone(),
|
||||
renderer.name().to_string());
|
||||
let preprocess_ctx = PreprocessorContext::new(
|
||||
self.root.clone(),
|
||||
self.config.clone(),
|
||||
renderer.name().to_string(),
|
||||
);
|
||||
|
||||
for preprocessor in &self.preprocessors {
|
||||
if preprocessor_should_run(&**preprocessor, renderer, &self.config) {
|
||||
debug!("Running the {} preprocessor.", preprocessor.name());
|
||||
preprocessed_book =
|
||||
preprocessor.run(&preprocess_ctx, preprocessed_book)?;
|
||||
preprocessed_book = preprocessor.run(&preprocess_ctx, preprocessed_book)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +180,7 @@ impl MDBook {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
preprocessed_book: &Book,
|
||||
renderer: &Renderer,
|
||||
) -> Result<()> {
|
||||
fn render(&self, preprocessed_book: &Book, renderer: &Renderer) -> Result<()> {
|
||||
let name = renderer.name();
|
||||
let build_dir = self.build_dir_for(name);
|
||||
if build_dir.exists() {
|
||||
@@ -233,9 +231,8 @@ impl MDBook {
|
||||
let temp_dir = TempFileBuilder::new().prefix("mdbook-").tempdir()?;
|
||||
|
||||
// FIXME: Is "test" the proper renderer name to use here?
|
||||
let preprocess_context = PreprocessorContext::new(self.root.clone(),
|
||||
self.config.clone(),
|
||||
"test".to_string());
|
||||
let preprocess_context =
|
||||
PreprocessorContext::new(self.root.clone(), self.config.clone(), "test".to_string());
|
||||
|
||||
let book = LinkPreprocessor::new().run(&preprocess_context, self.book.clone())?;
|
||||
// Index Preprocessor is disabled so that chapter paths continue to point to the
|
||||
@@ -363,17 +360,11 @@ fn determine_preprocessors(config: &Config) -> Result<Vec<Box<Preprocessor>>> {
|
||||
preprocessors.extend(default_preprocessors());
|
||||
}
|
||||
|
||||
if let Some(preprocessor_table) =
|
||||
config.get("preprocessor").and_then(|v| v.as_table())
|
||||
{
|
||||
if let Some(preprocessor_table) = config.get("preprocessor").and_then(|v| v.as_table()) {
|
||||
for key in preprocessor_table.keys() {
|
||||
match key.as_ref() {
|
||||
"links" => {
|
||||
preprocessors.push(Box::new(LinkPreprocessor::new()))
|
||||
}
|
||||
"index" => {
|
||||
preprocessors.push(Box::new(IndexPreprocessor::new()))
|
||||
}
|
||||
"links" => preprocessors.push(Box::new(LinkPreprocessor::new())),
|
||||
"index" => preprocessors.push(Box::new(IndexPreprocessor::new())),
|
||||
name => preprocessors.push(interpret_custom_preprocessor(
|
||||
name,
|
||||
&preprocessor_table[name],
|
||||
@@ -385,10 +376,7 @@ fn determine_preprocessors(config: &Config) -> Result<Vec<Box<Preprocessor>>> {
|
||||
Ok(preprocessors)
|
||||
}
|
||||
|
||||
fn interpret_custom_preprocessor(
|
||||
key: &str,
|
||||
table: &Value,
|
||||
) -> Box<CmdPreprocessor> {
|
||||
fn interpret_custom_preprocessor(key: &str, table: &Value) -> Box<CmdPreprocessor> {
|
||||
let command = table
|
||||
.get("command")
|
||||
.and_then(|c| c.as_str())
|
||||
@@ -406,8 +394,7 @@ fn interpret_custom_renderer(key: &str, table: &Value) -> Box<CmdRenderer> {
|
||||
.and_then(|c| c.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let command =
|
||||
table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));
|
||||
let command = table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));
|
||||
|
||||
Box::new(CmdRenderer::new(key.to_string(), command.to_string()))
|
||||
}
|
||||
@@ -428,7 +415,8 @@ fn preprocessor_should_run(preprocessor: &Preprocessor, renderer: &Renderer, cfg
|
||||
let renderer_name = renderer.name();
|
||||
|
||||
if let Some(Value::Array(ref explicit_renderers)) = cfg.get(&key) {
|
||||
return explicit_renderers.into_iter()
|
||||
return explicit_renderers
|
||||
.iter()
|
||||
.filter_map(|val| val.as_str())
|
||||
.any(|name| name == renderer_name);
|
||||
}
|
||||
@@ -436,7 +424,6 @@ fn preprocessor_should_run(preprocessor: &Preprocessor, renderer: &Renderer, cfg
|
||||
preprocessor.supports_renderer(renderer_name)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -539,10 +526,7 @@ mod tests {
|
||||
|
||||
// make sure the `preprocessor.random` table exists
|
||||
let random = cfg.get_preprocessor("random").unwrap();
|
||||
let random = interpret_custom_preprocessor(
|
||||
"random",
|
||||
&Value::Table(random.clone()),
|
||||
);
|
||||
let random = interpret_custom_preprocessor("random", &Value::Table(random.clone()));
|
||||
|
||||
assert_eq!(random.cmd(), "python random.py");
|
||||
}
|
||||
@@ -557,7 +541,8 @@ mod tests {
|
||||
let cfg = Config::from_str(cfg_str).unwrap();
|
||||
|
||||
// double-check that we can access preprocessor.links.renderers[0]
|
||||
let html = cfg.get_preprocessor("links")
|
||||
let html = cfg
|
||||
.get_preprocessor("links")
|
||||
.and_then(|links| links.get("renderers"))
|
||||
.and_then(|renderers| renderers.as_array())
|
||||
.and_then(|renderers| renderers.get(0))
|
||||
|
||||
@@ -280,7 +280,7 @@ impl<'a> SummaryParser<'a> {
|
||||
Err(self.parse_error("You can't have an empty link."))
|
||||
} else {
|
||||
Ok(Link {
|
||||
name: name,
|
||||
name,
|
||||
location: PathBuf::from(href.to_string()),
|
||||
number: None,
|
||||
nested_items: Vec::new(),
|
||||
@@ -313,7 +313,6 @@ impl<'a> SummaryParser<'a> {
|
||||
root_items += bunch_of_items.len() as u32;
|
||||
items.extend(bunch_of_items);
|
||||
|
||||
|
||||
match self.next_event() {
|
||||
Some(Event::Start(Tag::Paragraph)) => {
|
||||
// we're starting the suffix chapters
|
||||
@@ -732,7 +731,8 @@ mod tests {
|
||||
/// Ensure section numbers are correctly incremented after a horizontal separator.
|
||||
#[test]
|
||||
fn keep_numbering_after_separator() {
|
||||
let src = "- [First](./first.md)\n---\n- [Second](./second.md)\n---\n- [Third](./third.md)\n";
|
||||
let src =
|
||||
"- [First](./first.md)\n---\n- [Second](./second.md)\n---\n- [Third](./third.md)\n";
|
||||
let should_be = vec![
|
||||
SummaryItem::Link(Link {
|
||||
name: String::from("First"),
|
||||
|
||||
@@ -9,7 +9,8 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
.about("Builds a book from its markdown files")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
|
||||
).arg_from_usage(
|
||||
"[dir] 'Root directory for the book{n}\
|
||||
(Defaults to the Current Directory when omitted)'",
|
||||
|
||||
@@ -10,7 +10,9 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
.about("Deletes a built book")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
||||
Running this command deletes this directory.{n}\
|
||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
|
||||
).arg_from_usage(
|
||||
"[dir] 'Root directory for the book{n}\
|
||||
(Defaults to the Current Directory when omitted)'",
|
||||
|
||||
@@ -12,9 +12,10 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
SubCommand::with_name("init")
|
||||
.about("Creates the boilerplate structure and files for a new book")
|
||||
// the {n} denotes a newline which will properly aligned in all help messages
|
||||
.arg_from_usage("[dir] 'Directory to create the book in{n}\
|
||||
(Defaults to the Current Directory when omitted)'")
|
||||
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
|
||||
.arg_from_usage(
|
||||
"[dir] 'Directory to create the book in{n}\
|
||||
(Defaults to the Current Directory when omitted)'",
|
||||
).arg_from_usage("--theme 'Copies the default theme into your source folder'")
|
||||
.arg_from_usage("--force 'Skips confirmation prompts'")
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
|
||||
)
|
||||
.arg_from_usage(
|
||||
"[dir] 'Root directory for the book{n}\
|
||||
@@ -75,7 +76,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||
let port = args.value_of("port").unwrap();
|
||||
let ws_port = args.value_of("websocket-port").unwrap();
|
||||
let hostname = args.value_of("hostname").unwrap();
|
||||
let public_address = args.value_of("websocket-address").unwrap_or(hostname);
|
||||
let public_address = args.value_of("websocket-hostname").unwrap_or(hostname);
|
||||
let open_browser = args.is_present("open");
|
||||
|
||||
let address = format!("{}:{}", hostname, port);
|
||||
@@ -114,7 +115,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
watch::trigger_on_change(&mut book, move |path, book_dir| {
|
||||
watch::trigger_on_change(&book, move |path, book_dir| {
|
||||
info!("File changed: {:?}", path);
|
||||
info!("Building book...");
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
.about("Tests that a book's Rust code samples compile")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
|
||||
)
|
||||
.arg_from_usage(
|
||||
"[dir] 'Root directory for the book{n}\
|
||||
|
||||
@@ -16,7 +16,8 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
.about("Watches a book's files and rebuilds it on changes")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
|
||||
).arg_from_usage(
|
||||
"[dir] 'Root directory for the book{n}\
|
||||
(Defaults to the Current Directory when omitted)'",
|
||||
|
||||
@@ -301,8 +301,8 @@ impl<'de> Deserialize<'de> for Config {
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Config {
|
||||
book: book,
|
||||
build: build,
|
||||
book,
|
||||
build,
|
||||
rest: Value::Table(table),
|
||||
})
|
||||
}
|
||||
@@ -415,6 +415,8 @@ impl Default for BuildConfig {
|
||||
pub struct HtmlConfig {
|
||||
/// The theme directory, if specified.
|
||||
pub theme: Option<PathBuf>,
|
||||
/// The default theme to use, defaults to 'light'
|
||||
pub default_theme: Option<String>,
|
||||
/// Use "smart quotes" instead of the usual `"` character.
|
||||
pub curly_quotes: bool,
|
||||
/// Should mathjax be enabled?
|
||||
@@ -436,10 +438,15 @@ pub struct HtmlConfig {
|
||||
/// This config item *should not be edited* by the end user.
|
||||
#[doc(hidden)]
|
||||
pub livereload_url: Option<String>,
|
||||
/// Should section labels be rendered?
|
||||
/// Don't render section labels.
|
||||
pub no_section_label: bool,
|
||||
/// Search settings. If `None`, the default will be used.
|
||||
pub search: Option<Search>,
|
||||
/// Git repository url. If `None`, the git button will not be shown.
|
||||
pub git_repository_url: Option<String>,
|
||||
/// FontAwesome icon class to use for the Git repository link.
|
||||
/// Defaults to `fa-github` if `None`.
|
||||
pub git_repository_icon: Option<String>,
|
||||
}
|
||||
|
||||
impl HtmlConfig {
|
||||
@@ -568,9 +575,12 @@ mod tests {
|
||||
|
||||
[output.html]
|
||||
theme = "./themedir"
|
||||
default-theme = "rust"
|
||||
curly-quotes = true
|
||||
google-analytics = "123456"
|
||||
additional-css = ["./foo/bar/baz.css"]
|
||||
git-repository-url = "https://foo.com/"
|
||||
git-repository-icon = "fa-code-fork"
|
||||
|
||||
[output.html.playpen]
|
||||
editable = true
|
||||
@@ -607,7 +617,10 @@ mod tests {
|
||||
google_analytics: Some(String::from("123456")),
|
||||
additional_css: vec![PathBuf::from("./foo/bar/baz.css")],
|
||||
theme: Some(PathBuf::from("./themedir")),
|
||||
default_theme: Some(String::from("rust")),
|
||||
playpen: playpen_should_be,
|
||||
git_repository_url: Some(String::from("https://foo.com/")),
|
||||
git_repository_icon: Some(String::from("fa-code-fork")),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
mod cmd;
|
||||
|
||||
const NAME: &'static str = "mdBook";
|
||||
const VERSION: &'static str = concat!("v", crate_version!());
|
||||
const NAME: &str = "mdBook";
|
||||
const VERSION: &str = concat!("v", crate_version!());
|
||||
|
||||
fn main() {
|
||||
init_logger();
|
||||
|
||||
@@ -43,19 +43,11 @@ impl CmdPreprocessor {
|
||||
|
||||
/// A convenience function custom preprocessors can use to parse the input
|
||||
/// written to `stdin` by a `CmdRenderer`.
|
||||
pub fn parse_input<R: Read>(
|
||||
reader: R,
|
||||
) -> Result<(PreprocessorContext, Book)> {
|
||||
serde_json::from_reader(reader)
|
||||
.chain_err(|| "Unable to parse the input")
|
||||
pub fn parse_input<R: Read>(reader: R) -> Result<(PreprocessorContext, Book)> {
|
||||
serde_json::from_reader(reader).chain_err(|| "Unable to parse the input")
|
||||
}
|
||||
|
||||
fn write_input_to_child(
|
||||
&self,
|
||||
child: &mut Child,
|
||||
book: &Book,
|
||||
ctx: &PreprocessorContext,
|
||||
) {
|
||||
fn write_input_to_child(&self, child: &mut Child, book: &Book, ctx: &PreprocessorContext) {
|
||||
let stdin = child.stdin.take().expect("Child has stdin");
|
||||
|
||||
if let Err(e) = self.write_input(stdin, &book, &ctx) {
|
||||
@@ -65,9 +57,13 @@ impl CmdPreprocessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_input<W: Write>(&self, writer: W, book: &Book, ctx: &PreprocessorContext) -> Result<()> {
|
||||
serde_json::to_writer(writer, &(ctx, book))
|
||||
.map_err(Into::into)
|
||||
fn write_input<W: Write>(
|
||||
&self,
|
||||
writer: W,
|
||||
book: &Book,
|
||||
ctx: &PreprocessorContext,
|
||||
) -> Result<()> {
|
||||
serde_json::to_writer(writer, &(ctx, book)).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// The command this `Preprocessor` will invoke.
|
||||
@@ -105,7 +101,12 @@ impl Preprocessor for CmdPreprocessor {
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
.chain_err(|| format!("Unable to start the \"{}\" preprocessor. Is it installed?", self.name()))?;
|
||||
.chain_err(|| {
|
||||
format!(
|
||||
"Unable to start the \"{}\" preprocessor. Is it installed?",
|
||||
self.name()
|
||||
)
|
||||
})?;
|
||||
|
||||
self.write_input_to_child(&mut child, &book, ctx);
|
||||
|
||||
@@ -114,18 +115,29 @@ impl Preprocessor for CmdPreprocessor {
|
||||
.chain_err(|| "Error waiting for the preprocessor to complete")?;
|
||||
|
||||
trace!("{} exited with output: {:?}", self.cmd, output);
|
||||
ensure!(output.status.success(), "The preprocessor exited unsuccessfully");
|
||||
ensure!(
|
||||
output.status.success(),
|
||||
"The preprocessor exited unsuccessfully"
|
||||
);
|
||||
|
||||
serde_json::from_slice(&output.stdout).chain_err(|| "Unable to parse the preprocessed book")
|
||||
}
|
||||
|
||||
fn supports_renderer(&self, renderer: &str) -> bool {
|
||||
debug!("Checking if the \"{}\" preprocessor supports \"{}\"", self.name(), renderer);
|
||||
debug!(
|
||||
"Checking if the \"{}\" preprocessor supports \"{}\"",
|
||||
self.name(),
|
||||
renderer
|
||||
);
|
||||
|
||||
let mut cmd = match self.command() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
warn!("Unable to create the command for the \"{}\" preprocessor, {}", self.name(), e);
|
||||
warn!(
|
||||
"Unable to create the command for the \"{}\" preprocessor, {}",
|
||||
self.name(),
|
||||
e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -152,3 +164,34 @@ impl Preprocessor for CmdPreprocessor {
|
||||
outcome.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::path::Path;
|
||||
use MDBook;
|
||||
|
||||
fn book_example() -> MDBook {
|
||||
let example = Path::new(env!("CARGO_MANIFEST_DIR")).join("book-example");
|
||||
MDBook::load(example).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_write_and_parse_input() {
|
||||
let cmd = CmdPreprocessor::new("test".to_string(), "test".to_string());
|
||||
let md = book_example();
|
||||
let ctx = PreprocessorContext::new(
|
||||
md.root.clone(),
|
||||
md.config.clone(),
|
||||
"some-renderer".to_string(),
|
||||
);
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
cmd.write_input(&mut buffer, &md.book, &ctx).unwrap();
|
||||
|
||||
let (got_ctx, got_book) = CmdPreprocessor::parse_input(buffer.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(got_book, md.book);
|
||||
assert_eq!(got_ctx, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::{Preprocessor, PreprocessorContext};
|
||||
use book::{Book, BookItem};
|
||||
|
||||
/// A preprocessor for converting file name `README.md` to `index.md` since
|
||||
/// `README.md` is the de facto index file in a markdown-based documentation.
|
||||
/// `README.md` is the de facto index file in markdown-based documentation.
|
||||
pub struct IndexPreprocessor;
|
||||
|
||||
impl IndexPreprocessor {
|
||||
|
||||
@@ -69,12 +69,7 @@ where
|
||||
Ok(new_content) => {
|
||||
if depth < MAX_LINK_NESTED_DEPTH {
|
||||
if let Some(rel_path) = playpen.link.relative_path(path) {
|
||||
replaced.push_str(&replace_all(
|
||||
&new_content,
|
||||
rel_path,
|
||||
source,
|
||||
depth + 1,
|
||||
));
|
||||
replaced.push_str(&replace_all(&new_content, rel_path, source, depth + 1));
|
||||
} else {
|
||||
replaced.push_str(&new_content);
|
||||
}
|
||||
@@ -118,18 +113,10 @@ impl<'a> LinkType<'a> {
|
||||
let base = base.as_ref();
|
||||
match self {
|
||||
LinkType::Escaped => None,
|
||||
LinkType::IncludeRange(p, _) => {
|
||||
Some(return_relative_path(base, &p))
|
||||
}
|
||||
LinkType::IncludeRangeFrom(p, _) => {
|
||||
Some(return_relative_path(base, &p))
|
||||
}
|
||||
LinkType::IncludeRangeTo(p, _) => {
|
||||
Some(return_relative_path(base, &p))
|
||||
}
|
||||
LinkType::IncludeRangeFull(p, _) => {
|
||||
Some(return_relative_path(base, &p))
|
||||
}
|
||||
LinkType::IncludeRange(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::IncludeRangeFrom(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::IncludeRangeTo(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::IncludeRangeFull(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::Playpen(p, _) => Some(return_relative_path(base, &p)),
|
||||
}
|
||||
}
|
||||
@@ -155,27 +142,21 @@ fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||
let end = end.and_then(|s| s.parse::<usize>().ok());
|
||||
match start {
|
||||
Some(start) => match end {
|
||||
Some(end) => LinkType::IncludeRange(
|
||||
path,
|
||||
Range {
|
||||
start: start,
|
||||
end: end,
|
||||
},
|
||||
),
|
||||
Some(end) => LinkType::IncludeRange(path, Range { start, end }),
|
||||
None => if has_end {
|
||||
LinkType::IncludeRangeFrom(path, RangeFrom { start: start })
|
||||
LinkType::IncludeRangeFrom(path, RangeFrom { start })
|
||||
} else {
|
||||
LinkType::IncludeRange(
|
||||
path,
|
||||
Range {
|
||||
start: start,
|
||||
start,
|
||||
end: start + 1,
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
None => match end {
|
||||
Some(end) => LinkType::IncludeRangeTo(path, RangeTo { end: end }),
|
||||
Some(end) => LinkType::IncludeRangeTo(path, RangeTo { end }),
|
||||
None => LinkType::IncludeRangeFull(path, RangeFull),
|
||||
},
|
||||
}
|
||||
@@ -199,15 +180,11 @@ impl<'a> Link<'a> {
|
||||
|
||||
match (typ.as_str(), file_arg) {
|
||||
("include", Some(pth)) => Some(parse_include_path(pth)),
|
||||
("playpen", Some(pth)) => {
|
||||
Some(LinkType::Playpen(pth.into(), props))
|
||||
}
|
||||
("playpen", Some(pth)) => Some(LinkType::Playpen(pth.into(), props)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
(Some(mat), None, None)
|
||||
if mat.as_str().starts_with(ESCAPE_CHAR) =>
|
||||
{
|
||||
(Some(mat), None, None) if mat.as_str().starts_with(ESCAPE_CHAR) => {
|
||||
Some(LinkType::Escaped)
|
||||
}
|
||||
_ => None,
|
||||
@@ -258,7 +235,7 @@ impl<'a> Link<'a> {
|
||||
let target = base.join(pat);
|
||||
|
||||
file_to_string(&target)
|
||||
.map(|s| take_lines(&s, range.clone()))
|
||||
.map(|s| take_lines(&s, *range))
|
||||
.chain_err(|| {
|
||||
format!(
|
||||
"Could not read file for link {} ({})",
|
||||
@@ -271,22 +248,23 @@ impl<'a> Link<'a> {
|
||||
let target = base.join(pat);
|
||||
|
||||
file_to_string(&target).chain_err(|| {
|
||||
format!("Could not read file for link {} ({})",
|
||||
self.link_text,
|
||||
target.display())
|
||||
format!(
|
||||
"Could not read file for link {} ({})",
|
||||
self.link_text,
|
||||
target.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
LinkType::Playpen(ref pat, ref attrs) => {
|
||||
let target = base.join(pat);
|
||||
|
||||
let contents =
|
||||
file_to_string(&target).chain_err(|| {
|
||||
format!(
|
||||
"Could not read file for link {} ({})",
|
||||
self.link_text,
|
||||
target.display()
|
||||
)
|
||||
})?;
|
||||
let contents = file_to_string(&target).chain_err(|| {
|
||||
format!(
|
||||
"Could not read file for link {} ({})",
|
||||
self.link_text,
|
||||
target.display()
|
||||
)
|
||||
})?;
|
||||
let ftype = if !attrs.is_empty() { "rust," } else { "rust" };
|
||||
Ok(format!(
|
||||
"```{}{}\n{}\n```\n",
|
||||
@@ -531,10 +509,7 @@ mod tests {
|
||||
Link {
|
||||
start_index: 38,
|
||||
end_index: 68,
|
||||
link: LinkType::Playpen(
|
||||
PathBuf::from("file.rs"),
|
||||
vec!["editable"]
|
||||
),
|
||||
link: LinkType::Playpen(PathBuf::from("file.rs"), vec!["editable"]),
|
||||
link_text: "{{#playpen file.rs editable }}",
|
||||
},
|
||||
Link {
|
||||
@@ -544,8 +519,7 @@ mod tests {
|
||||
PathBuf::from("my.rs"),
|
||||
vec!["editable", "no_run", "should_panic"],
|
||||
),
|
||||
link_text:
|
||||
"{{#playpen my.rs editable no_run should_panic}}",
|
||||
link_text: "{{#playpen my.rs editable no_run should_panic}}",
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! Book preprocessing.
|
||||
|
||||
pub use self::cmd::CmdPreprocessor;
|
||||
pub use self::index::IndexPreprocessor;
|
||||
pub use self::links::LinkPreprocessor;
|
||||
pub use self::cmd::CmdPreprocessor;
|
||||
|
||||
mod cmd;
|
||||
mod index;
|
||||
mod links;
|
||||
mod cmd;
|
||||
|
||||
use book::Book;
|
||||
use config::Config;
|
||||
@@ -26,6 +26,7 @@ pub struct PreprocessorContext {
|
||||
pub renderer: String,
|
||||
/// The calling `mdbook` version.
|
||||
pub mdbook_version: String,
|
||||
#[serde(skip)]
|
||||
__non_exhaustive: (),
|
||||
}
|
||||
|
||||
|
||||
@@ -30,75 +30,71 @@ impl HtmlHandlebars {
|
||||
print_content: &mut String,
|
||||
) -> Result<()> {
|
||||
// FIXME: This should be made DRY-er and rely less on mutable state
|
||||
match *item {
|
||||
BookItem::Chapter(ref ch) => {
|
||||
let content = ch.content.clone();
|
||||
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
|
||||
print_content.push_str(&content);
|
||||
if let BookItem::Chapter(ref ch) = *item {
|
||||
let content = ch.content.clone();
|
||||
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
|
||||
|
||||
// Update the context with data for this file
|
||||
let path = ch
|
||||
.path
|
||||
.to_str()
|
||||
.chain_err(|| "Could not convert path to str")?;
|
||||
let filepath = Path::new(&ch.path).with_extension("html");
|
||||
let string_path = ch.path.parent().unwrap().display().to_string();
|
||||
|
||||
// "print.html" is used for the print page.
|
||||
if ch.path == Path::new("print.md") {
|
||||
bail!(ErrorKind::ReservedFilenameError(ch.path.clone()));
|
||||
};
|
||||
let fixed_content = utils::render_markdown_with_base(&ch.content, ctx.html_config.curly_quotes, &string_path);
|
||||
print_content.push_str(&fixed_content);
|
||||
|
||||
// Non-lexical lifetimes needed :'(
|
||||
let title: String;
|
||||
{
|
||||
let book_title = ctx
|
||||
.data
|
||||
.get("book_title")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or("");
|
||||
title = ch.name.clone() + " - " + book_title;
|
||||
}
|
||||
// Update the context with data for this file
|
||||
let path = ch
|
||||
.path
|
||||
.to_str()
|
||||
.chain_err(|| "Could not convert path to str")?;
|
||||
let filepath = Path::new(&ch.path).with_extension("html");
|
||||
|
||||
ctx.data.insert("path".to_owned(), json!(path));
|
||||
ctx.data.insert("content".to_owned(), json!(content));
|
||||
ctx.data.insert("chapter_title".to_owned(), json!(ch.name));
|
||||
ctx.data.insert("title".to_owned(), json!(title));
|
||||
ctx.data.insert(
|
||||
"path_to_root".to_owned(),
|
||||
json!(utils::fs::path_to_root(&ch.path)),
|
||||
);
|
||||
// "print.html" is used for the print page.
|
||||
if ch.path == Path::new("print.md") {
|
||||
bail!(ErrorKind::ReservedFilenameError(ch.path.clone()));
|
||||
};
|
||||
|
||||
// Render the handlebars template with the data
|
||||
debug!("Render template");
|
||||
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
||||
|
||||
let rendered = self.post_process(rendered, &ctx.html_config.playpen);
|
||||
|
||||
// Write to file
|
||||
debug!("Creating {}", filepath.display());
|
||||
utils::fs::write_file(&ctx.destination, &filepath, rendered.as_bytes())?;
|
||||
|
||||
if ctx.is_index {
|
||||
ctx.data.insert("path".to_owned(), json!("index.html"));
|
||||
ctx.data.insert("path_to_root".to_owned(), json!(""));
|
||||
let rendered_index = ctx.handlebars.render("index", &ctx.data)?;
|
||||
let rendered_index =
|
||||
self.post_process(rendered_index, &ctx.html_config.playpen);
|
||||
debug!("Creating index.html from {}", path);
|
||||
utils::fs::write_file(
|
||||
&ctx.destination,
|
||||
"index.html",
|
||||
rendered_index.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
// Non-lexical lifetimes needed :'(
|
||||
let title: String;
|
||||
{
|
||||
let book_title = ctx
|
||||
.data
|
||||
.get("book_title")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or("");
|
||||
title = ch.name.clone() + " - " + book_title;
|
||||
}
|
||||
|
||||
ctx.data.insert("path".to_owned(), json!(path));
|
||||
ctx.data.insert("content".to_owned(), json!(content));
|
||||
ctx.data.insert("chapter_title".to_owned(), json!(ch.name));
|
||||
ctx.data.insert("title".to_owned(), json!(title));
|
||||
ctx.data.insert(
|
||||
"path_to_root".to_owned(),
|
||||
json!(utils::fs::path_to_root(&ch.path)),
|
||||
);
|
||||
|
||||
// Render the handlebars template with the data
|
||||
debug!("Render template");
|
||||
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
||||
|
||||
let rendered = self.post_process(rendered, &ctx.html_config.playpen);
|
||||
|
||||
// Write to file
|
||||
debug!("Creating {}", filepath.display());
|
||||
utils::fs::write_file(&ctx.destination, &filepath, rendered.as_bytes())?;
|
||||
|
||||
if ctx.is_index {
|
||||
ctx.data.insert("path".to_owned(), json!("index.html"));
|
||||
ctx.data.insert("path_to_root".to_owned(), json!(""));
|
||||
let rendered_index = ctx.handlebars.render("index", &ctx.data)?;
|
||||
let rendered_index = self.post_process(rendered_index, &ctx.html_config.playpen);
|
||||
debug!("Creating index.html from {}", path);
|
||||
utils::fs::write_file(&ctx.destination, "index.html", rendered_index.as_bytes())?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::let_and_return))]
|
||||
fn post_process(&self, rendered: String, playpen_config: &Playpen) -> String {
|
||||
let rendered = build_header_links(&rendered);
|
||||
let rendered = fix_code_blocks(&rendered);
|
||||
@@ -214,6 +210,7 @@ impl HtmlHandlebars {
|
||||
);
|
||||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||
handlebars.register_helper("theme_option", Box::new(helpers::theme::theme_option));
|
||||
}
|
||||
|
||||
/// Copy across any additional CSS and JavaScript files which the book
|
||||
@@ -327,7 +324,7 @@ impl Renderer for HtmlHandlebars {
|
||||
handlebars: &handlebars,
|
||||
destination: destination.to_path_buf(),
|
||||
data: data.clone(),
|
||||
is_index: is_index,
|
||||
is_index,
|
||||
html_config: html_config.clone(),
|
||||
};
|
||||
self.render_item(item, ctx, &mut print_content)?;
|
||||
@@ -395,6 +392,12 @@ fn make_data(
|
||||
data.insert("livereload".to_owned(), json!(livereload));
|
||||
}
|
||||
|
||||
let default_theme = match html_config.default_theme {
|
||||
Some(ref theme) => theme,
|
||||
None => "light",
|
||||
};
|
||||
data.insert("default_theme".to_owned(), json!(default_theme));
|
||||
|
||||
// Add google analytics tag
|
||||
if let Some(ref ga) = config.html_config().and_then(|html| html.google_analytics) {
|
||||
data.insert("google_analytics".to_owned(), json!(ga));
|
||||
@@ -454,6 +457,15 @@ fn make_data(
|
||||
)
|
||||
}
|
||||
|
||||
if let Some(ref git_repository_url) = html_config.git_repository_url {
|
||||
data.insert("git_repository_url".to_owned(), json!(git_repository_url));
|
||||
}
|
||||
let git_repository_icon = match html_config.git_repository_icon {
|
||||
Some(ref git_repository_icon) => git_repository_icon,
|
||||
None => "fa-github",
|
||||
};
|
||||
data.insert("git_repository_icon".to_owned(), json!(git_repository_icon));
|
||||
|
||||
let mut chapters = vec![];
|
||||
|
||||
for item in book.iter() {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod navigation;
|
||||
pub mod theme;
|
||||
pub mod toc;
|
||||
|
||||
@@ -18,13 +18,13 @@ impl Target {
|
||||
/// Returns target if found.
|
||||
fn find(
|
||||
&self,
|
||||
base_path: &String,
|
||||
current_path: &String,
|
||||
base_path: &str,
|
||||
current_path: &str,
|
||||
current_item: &StringMap,
|
||||
previous_item: &StringMap,
|
||||
) -> Result<Option<StringMap>, RenderError> {
|
||||
match self {
|
||||
&Target::Next => {
|
||||
match *self {
|
||||
Target::Next => {
|
||||
let previous_path = previous_item
|
||||
.get("path")
|
||||
.ok_or_else(|| RenderError::new("No path found for chapter in JSON data"))?;
|
||||
@@ -34,7 +34,7 @@ impl Target {
|
||||
}
|
||||
}
|
||||
|
||||
&Target::Previous => {
|
||||
Target::Previous => {
|
||||
if current_path == base_path {
|
||||
return Ok(Some(previous_item.clone()));
|
||||
}
|
||||
|
||||
30
src/renderer/html_handlebars/helpers/theme.rs
Normal file
30
src/renderer/html_handlebars/helpers/theme.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
|
||||
|
||||
pub fn theme_option(
|
||||
h: &Helper,
|
||||
_r: &Handlebars,
|
||||
ctx: &Context,
|
||||
rc: &mut RenderContext,
|
||||
out: &mut Output,
|
||||
) -> Result<(), RenderError> {
|
||||
trace!("theme_option (handlebars helper)");
|
||||
|
||||
let param = h
|
||||
.param(0)
|
||||
.and_then(|v| v.value().as_str())
|
||||
.ok_or(RenderError::new(
|
||||
"Param 0 with String type is required for theme_option helper.",
|
||||
))?;
|
||||
|
||||
let theme_name = rc
|
||||
.evaluate_absolute(ctx, "default_theme", true)?
|
||||
.as_str()
|
||||
.ok_or_else(|| RenderError::new("Type error for `default_theme`, string expected"))?;
|
||||
|
||||
out.write(param)?;
|
||||
if param.to_lowercase() == theme_name.to_lowercase() {
|
||||
out.write(" (default)")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -54,7 +54,7 @@ fn add_doc(
|
||||
section_id: &Option<String>,
|
||||
items: &[&str],
|
||||
) {
|
||||
let url = if let &Some(ref id) = section_id {
|
||||
let url = if let Some(ref id) = *section_id {
|
||||
Cow::Owned(format!("{}#{}", anchor_base, id))
|
||||
} else {
|
||||
Cow::Borrowed(anchor_base)
|
||||
@@ -74,8 +74,8 @@ fn render_item(
|
||||
doc_urls: &mut Vec<String>,
|
||||
item: &BookItem,
|
||||
) -> Result<()> {
|
||||
let chapter = match item {
|
||||
&BookItem::Chapter(ref ch) => ch,
|
||||
let chapter = match *item {
|
||||
BookItem::Chapter(ref ch) => ch,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
@@ -101,7 +101,7 @@ fn render_item(
|
||||
for event in p {
|
||||
match event {
|
||||
Event::Start(Tag::Header(i)) if i <= max_section_depth => {
|
||||
if heading.len() > 0 {
|
||||
if !heading.is_empty() {
|
||||
// Section finished, the next header is following now
|
||||
// Write the data to the index, and clear it for the next section
|
||||
add_doc(
|
||||
@@ -155,7 +155,7 @@ fn render_item(
|
||||
}
|
||||
}
|
||||
|
||||
if heading.len() > 0 {
|
||||
if !heading.is_empty() {
|
||||
// Make sure the last section is added to the index
|
||||
add_doc(
|
||||
index,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//!
|
||||
//! The definition for [RenderContext] may be useful though.
|
||||
//!
|
||||
//! [For Developers]: https://rust-lang-nursery.github.io/mdBook/lib/index.html
|
||||
//! [For Developers]: https://rust-lang-nursery.github.io/mdBook/for_developers/index.html
|
||||
//! [RenderContext]: struct.RenderContext.html
|
||||
|
||||
pub use self::html_handlebars::HtmlHandlebars;
|
||||
@@ -64,6 +64,7 @@ pub struct RenderContext {
|
||||
/// renderers to cache intermediate results, this directory is not
|
||||
/// guaranteed to be empty or even exist.
|
||||
pub destination: PathBuf,
|
||||
#[serde(skip)]
|
||||
__non_exhaustive: (),
|
||||
}
|
||||
|
||||
@@ -75,8 +76,8 @@ impl RenderContext {
|
||||
Q: Into<PathBuf>,
|
||||
{
|
||||
RenderContext {
|
||||
book: book,
|
||||
config: config,
|
||||
book,
|
||||
config,
|
||||
version: ::MDBOOK_VERSION.to_string(),
|
||||
root: root.into(),
|
||||
destination: destination.into(),
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -352,7 +352,7 @@ function playpen_text(playpen) {
|
||||
|
||||
var previousTheme;
|
||||
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||
if (previousTheme === null || previousTheme === undefined) { previousTheme = 'light'; }
|
||||
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
|
||||
|
||||
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
|
||||
|
||||
@@ -364,7 +364,7 @@ function playpen_text(playpen) {
|
||||
// Set theme
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
|
||||
set_theme(theme);
|
||||
|
||||
|
||||
@@ -63,9 +63,12 @@ a > .hljs {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#print-button {
|
||||
.right-buttons {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.right-buttons a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
|
||||
transform: translateY(-60px);
|
||||
|
||||
@@ -35,9 +35,12 @@
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
{{/if}}
|
||||
</head>
|
||||
<body class="light">
|
||||
<body class="{{ default_theme }}">
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">var path_to_root = "{{ path_to_root }}";</script>
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "{{ path_to_root }}";
|
||||
var default_theme = "{{ default_theme }}";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
@@ -59,7 +62,7 @@
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
document.body.className = theme;
|
||||
document.querySelector('html').className = theme + ' js';
|
||||
</script>
|
||||
@@ -94,11 +97,11 @@
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">{{ theme_option "Light" }}</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">{{ theme_option "Rust" }}</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">{{ theme_option "Coal" }}</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">{{ theme_option "Navy" }}</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">{{ theme_option "Ayu" }}</button></li>
|
||||
</ul>
|
||||
{{#if search_enabled}}
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
@@ -113,6 +116,11 @@
|
||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
{{#if git_repository_url}}
|
||||
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,9 +21,10 @@ pub fn collapse_whitespace<'a>(text: &'a str) -> Cow<'a, str> {
|
||||
RE.replace_all(text, " ")
|
||||
}
|
||||
|
||||
/// Convert the given string to a valid HTML element ID
|
||||
/// Convert the given string to a valid HTML element ID.
|
||||
/// The only restriction is that the ID must not contain any ASCII whitespace.
|
||||
pub fn normalize_id(content: &str) -> String {
|
||||
let mut ret = content
|
||||
content
|
||||
.chars()
|
||||
.filter_map(|ch| {
|
||||
if ch.is_alphanumeric() || ch == '_' || ch == '-' {
|
||||
@@ -33,16 +34,7 @@ pub fn normalize_id(content: &str) -> String {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect::<String>();
|
||||
// Ensure that the first character is [A-Za-z]
|
||||
if ret
|
||||
.chars()
|
||||
.next()
|
||||
.map_or(false, |c| !c.is_ascii_alphabetic())
|
||||
{
|
||||
ret.insert(0, 'a');
|
||||
}
|
||||
ret
|
||||
}).collect::<String>()
|
||||
}
|
||||
|
||||
/// Generate an ID for use with anchors which is derived from a "normalised"
|
||||
@@ -74,15 +66,21 @@ pub fn id_from_content(content: &str) -> String {
|
||||
normalize_id(trimmed)
|
||||
}
|
||||
|
||||
fn adjust_links(event: Event) -> Event {
|
||||
fn adjust_links<'a>(event: Event<'a>, with_base: &str) -> Event<'a> {
|
||||
lazy_static! {
|
||||
static ref HTTP_LINK: Regex = Regex::new("^https?://").unwrap();
|
||||
static ref MD_LINK: Regex = Regex::new("(?P<link>.*).md(?P<anchor>#.*)?").unwrap();
|
||||
static ref MD_LINK: Regex = Regex::new(r"(?P<link>.*)\.md(?P<anchor>#.*)?").unwrap();
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::Start(Tag::Link(dest, title)) => {
|
||||
if !HTTP_LINK.is_match(&dest) {
|
||||
let dest = if !with_base.is_empty() {
|
||||
format!("{}/{}", with_base, dest)
|
||||
} else {
|
||||
dest.clone().into_owned()
|
||||
};
|
||||
|
||||
if let Some(caps) = MD_LINK.captures(&dest) {
|
||||
let mut html_link = [&caps["link"], ".html"].concat();
|
||||
|
||||
@@ -102,6 +100,10 @@ fn adjust_links(event: Event) -> Event {
|
||||
|
||||
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
|
||||
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
|
||||
render_markdown_with_base(text, curly_quotes, "")
|
||||
}
|
||||
|
||||
pub fn render_markdown_with_base(text: &str, curly_quotes: bool, base: &str) -> String {
|
||||
let mut s = String::with_capacity(text.len() * 3 / 2);
|
||||
|
||||
let mut opts = Options::empty();
|
||||
@@ -112,7 +114,7 @@ pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
|
||||
let mut converter = EventQuoteConverter::new(curly_quotes);
|
||||
let events = p
|
||||
.map(clean_codeblock_headers)
|
||||
.map(adjust_links)
|
||||
.map(|event| adjust_links(event, base))
|
||||
.map(|event| converter.convert(event));
|
||||
|
||||
html::push_html(&mut s, events);
|
||||
@@ -127,7 +129,7 @@ struct EventQuoteConverter {
|
||||
impl EventQuoteConverter {
|
||||
fn new(enabled: bool) -> Self {
|
||||
EventQuoteConverter {
|
||||
enabled: enabled,
|
||||
enabled,
|
||||
convert_text: true,
|
||||
}
|
||||
}
|
||||
@@ -228,6 +230,12 @@ mod tests {
|
||||
render_markdown("[example_anchor](example.md#anchor)", false),
|
||||
"<p><a href=\"example.html#anchor\">example_anchor</a></p>\n"
|
||||
);
|
||||
|
||||
// this anchor contains 'md' inside of it
|
||||
assert_eq!(
|
||||
render_markdown("[phantom data](foo.html#phantomdata)", false),
|
||||
"<p><a href=\"foo.html#phantomdata\">phantom data</a></p>\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -328,28 +336,42 @@ more text with spaces
|
||||
|
||||
#[test]
|
||||
fn it_generates_anchors() {
|
||||
assert_eq!(
|
||||
id_from_content("## `--passes`: add more rustdoc passes"),
|
||||
"a--passes-add-more-rustdoc-passes"
|
||||
);
|
||||
assert_eq!(
|
||||
id_from_content("## Method-call expressions"),
|
||||
"method-call-expressions"
|
||||
);
|
||||
assert_eq!(id_from_content("## **Bold** title"), "bold-title");
|
||||
assert_eq!(id_from_content("## `Code` title"), "code-title");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_generates_anchors_from_non_ascii_initial() {
|
||||
assert_eq!(
|
||||
id_from_content("## `--passes`: add more rustdoc passes"),
|
||||
"--passes-add-more-rustdoc-passes"
|
||||
);
|
||||
assert_eq!(
|
||||
id_from_content("## 中文標題 CJK title"),
|
||||
"中文標題-cjk-title"
|
||||
);
|
||||
assert_eq!(id_from_content("## Über"), "Über");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_normalizes_ids() {
|
||||
assert_eq!(
|
||||
normalize_id("`--passes`: add more rustdoc passes"),
|
||||
"a--passes-add-more-rustdoc-passes"
|
||||
"--passes-add-more-rustdoc-passes"
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_id("Method-call 🐙 expressions \u{1f47c}"),
|
||||
"method-call--expressions-"
|
||||
);
|
||||
assert_eq!(normalize_id("_-_12345"), "a_-_12345");
|
||||
assert_eq!(normalize_id("12345"), "a12345");
|
||||
assert_eq!(normalize_id("_-_12345"), "_-_12345");
|
||||
assert_eq!(normalize_id("12345"), "12345");
|
||||
assert_eq!(normalize_id("中文"), "中文");
|
||||
assert_eq!(normalize_id("にほんご"), "にほんご");
|
||||
assert_eq!(normalize_id("한국어"), "한국어");
|
||||
assert_eq!(normalize_id(""), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@ use tempfile::{Builder as TempFileBuilder, TempDir};
|
||||
|
||||
#[test]
|
||||
fn passing_alternate_backend() {
|
||||
let (md, _temp) = dummy_book_with_backend("passing", "true");
|
||||
let (md, _temp) = dummy_book_with_backend("passing", success_cmd());
|
||||
|
||||
md.build().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_alternate_backend() {
|
||||
let (md, _temp) = dummy_book_with_backend("failing", "false");
|
||||
let (md, _temp) = dummy_book_with_backend("failing", fail_cmd());
|
||||
|
||||
md.build().unwrap_err();
|
||||
}
|
||||
@@ -84,3 +84,19 @@ fn dummy_book_with_backend(name: &str, command: &str) -> (MDBook, TempDir) {
|
||||
|
||||
(md, temp)
|
||||
}
|
||||
|
||||
fn fail_cmd() -> &'static str {
|
||||
if cfg!(windows) {
|
||||
r#"cmd.exe /c "exit 1""#
|
||||
} else {
|
||||
"false"
|
||||
}
|
||||
}
|
||||
|
||||
fn success_cmd() -> &'static str {
|
||||
if cfg!(windows) {
|
||||
r#"cmd.exe /c "exit 0""#
|
||||
} else {
|
||||
"true"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
|
||||
use mdbook::MDBook;
|
||||
|
||||
fn example() -> CmdPreprocessor {
|
||||
CmdPreprocessor::new("nop-preprocessor".to_string(), "cargo run --example nop-preprocessor --".to_string())
|
||||
CmdPreprocessor::new(
|
||||
"nop-preprocessor".to_string(),
|
||||
"cargo run --example nop-preprocessor --".to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -35,7 +38,9 @@ fn ask_the_preprocessor_to_blow_up() {
|
||||
let mut md = MDBook::load(temp.path()).unwrap();
|
||||
md.with_preprecessor(example());
|
||||
|
||||
md.config.set("preprocessor.nop-preprocessor.blow-up", true).unwrap();
|
||||
md.config
|
||||
.set("preprocessor.nop-preprocessor.blow-up", true)
|
||||
.unwrap();
|
||||
|
||||
let got = md.build();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- [Includes](first/includes.md)
|
||||
- [Recursive](first/recursive.md)
|
||||
- [Second Chapter](second.md)
|
||||
- [Nested Chapter](second/nested.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
4
tests/dummy_book/src/second/nested.md
Normal file
4
tests/dummy_book/src/second/nested.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Testing relative links for the print page
|
||||
|
||||
When we link to [the first section](../first/nested.md), it should work on
|
||||
both the print page and the non-print page.
|
||||
@@ -31,7 +31,7 @@ const TOC_TOP_LEVEL: &[&'static str] = &[
|
||||
"Introduction",
|
||||
];
|
||||
const TOC_SECOND_LEVEL: &[&'static str] =
|
||||
&["1.1. Nested Chapter", "1.2. Includes", "1.3. Recursive"];
|
||||
&["1.1. Nested Chapter", "1.2. Includes", "2.1. Nested Chapter", "1.3. Recursive"];
|
||||
|
||||
/// Make sure you can load the dummy book and build it without panicking.
|
||||
#[test]
|
||||
@@ -109,6 +109,20 @@ fn check_correct_cross_links_in_nested_dir() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_correct_relative_links_in_print_page() {
|
||||
let temp = DummyBook::new().build().unwrap();
|
||||
let md = MDBook::load(temp.path()).unwrap();
|
||||
md.build().unwrap();
|
||||
|
||||
let first = temp.path().join("book");
|
||||
|
||||
assert_contains_strings(
|
||||
first.join("print.html"),
|
||||
&[r##"<a href="second/../first/nested.html">the first section</a>,"##],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rendered_code_has_playpen_stuff() {
|
||||
let temp = DummyBook::new().build().unwrap();
|
||||
@@ -443,7 +457,7 @@ mod search {
|
||||
assert_eq!(docs[&some_section]["body"], "");
|
||||
assert_eq!(
|
||||
docs[&summary]["body"],
|
||||
"Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Second Chapter Conclusion"
|
||||
"Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Second Chapter Nested Chapter Conclusion"
|
||||
);
|
||||
assert_eq!(docs[&summary]["breadcrumbs"], "First Chapter » Summary");
|
||||
assert_eq!(docs[&conclusion]["body"], "I put <HTML> in here!");
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"first/includes.html#includes",
|
||||
"first/includes.html#summary",
|
||||
"second.html#second-chapter",
|
||||
"second/nested.html#testing-relative-links-for-the-print-page",
|
||||
"conclusion.html#conclusion"
|
||||
],
|
||||
"index": {
|
||||
@@ -24,6 +25,11 @@
|
||||
"breadcrumbs": 1,
|
||||
"title": 1
|
||||
},
|
||||
"10": {
|
||||
"body": 3,
|
||||
"breadcrumbs": 1,
|
||||
"title": 1
|
||||
},
|
||||
"2": {
|
||||
"body": 2,
|
||||
"breadcrumbs": 2,
|
||||
@@ -50,7 +56,7 @@
|
||||
"title": 1
|
||||
},
|
||||
"7": {
|
||||
"body": 12,
|
||||
"body": 14,
|
||||
"breadcrumbs": 3,
|
||||
"title": 1
|
||||
},
|
||||
@@ -60,9 +66,9 @@
|
||||
"title": 2
|
||||
},
|
||||
"9": {
|
||||
"body": 3,
|
||||
"breadcrumbs": 1,
|
||||
"title": 1
|
||||
"body": 10,
|
||||
"breadcrumbs": 7,
|
||||
"title": 5
|
||||
}
|
||||
},
|
||||
"docs": {
|
||||
@@ -78,6 +84,12 @@
|
||||
"id": "1",
|
||||
"title": "Introduction"
|
||||
},
|
||||
"10": {
|
||||
"body": "I put <HTML> in here!",
|
||||
"breadcrumbs": "Conclusion",
|
||||
"id": "10",
|
||||
"title": "Conclusion"
|
||||
},
|
||||
"2": {
|
||||
"body": "more text.",
|
||||
"breadcrumbs": "First Chapter",
|
||||
@@ -109,7 +121,7 @@
|
||||
"title": "Includes"
|
||||
},
|
||||
"7": {
|
||||
"body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Second Chapter Conclusion",
|
||||
"body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Second Chapter Nested Chapter Conclusion",
|
||||
"breadcrumbs": "First Chapter » Summary",
|
||||
"id": "7",
|
||||
"title": "Summary"
|
||||
@@ -121,13 +133,13 @@
|
||||
"title": "Second Chapter"
|
||||
},
|
||||
"9": {
|
||||
"body": "I put <HTML> in here!",
|
||||
"breadcrumbs": "Conclusion",
|
||||
"body": "When we link to the first section , it should work on both the print page and the non-print page.",
|
||||
"breadcrumbs": "Second Chapter » Testing relative links for the print page",
|
||||
"id": "9",
|
||||
"title": "Conclusion"
|
||||
"title": "Testing relative links for the print page"
|
||||
}
|
||||
},
|
||||
"length": 10,
|
||||
"length": 11,
|
||||
"save": true
|
||||
},
|
||||
"fields": [
|
||||
@@ -206,6 +218,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"t": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"h": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -251,7 +275,7 @@
|
||||
"tf": 1.0
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.7320508075688773
|
||||
"tf": 2.0
|
||||
},
|
||||
"8": {
|
||||
"tf": 1.0
|
||||
@@ -296,10 +320,10 @@
|
||||
"s": {
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"7": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -420,13 +444,16 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 2,
|
||||
"df": 3,
|
||||
"docs": {
|
||||
"2": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,7 +512,7 @@
|
||||
"0": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -683,6 +710,14 @@
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"k": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -709,7 +744,7 @@
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -791,14 +826,42 @@
|
||||
"tf": 1.0
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"o": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"p": {
|
||||
"a": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"g": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"e": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.7320508075688772
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"r": {
|
||||
@@ -871,8 +934,12 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.7320508075688772
|
||||
}
|
||||
},
|
||||
"l": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
@@ -935,7 +1002,7 @@
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -967,7 +1034,15 @@
|
||||
}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
"docs": {},
|
||||
"l": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"df": 0,
|
||||
@@ -1050,13 +1125,16 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 2,
|
||||
"df": 3,
|
||||
"docs": {
|
||||
"3": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"5": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1170,8 +1248,12 @@
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x": {
|
||||
@@ -1200,6 +1282,14 @@
|
||||
"r": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"k": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"l": {
|
||||
"d": {
|
||||
"df": 1,
|
||||
@@ -1280,13 +1370,25 @@
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"0": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"t": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"h": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1323,13 +1425,13 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"r": {
|
||||
"df": 6,
|
||||
"df": 7,
|
||||
"docs": {
|
||||
"2": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"4": {
|
||||
"tf": 1.7320508075688773
|
||||
"tf": 1.7320508075688772
|
||||
},
|
||||
"5": {
|
||||
"tf": 1.0
|
||||
@@ -1338,10 +1440,13 @@
|
||||
"tf": 1.0
|
||||
},
|
||||
"7": {
|
||||
"tf": 2.0
|
||||
"tf": 2.23606797749979
|
||||
},
|
||||
"8": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1383,11 +1488,11 @@
|
||||
"s": {
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"10": {
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.4142135623730952
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1419,7 +1524,7 @@
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"0": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
@@ -1507,10 +1612,10 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 5,
|
||||
"df": 6,
|
||||
"docs": {
|
||||
"2": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"4": {
|
||||
"tf": 1.0
|
||||
@@ -1522,7 +1627,10 @@
|
||||
"tf": 1.0
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1581,7 +1689,7 @@
|
||||
"0": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -1636,7 +1744,7 @@
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"6": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
@@ -1728,7 +1836,7 @@
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"1": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
@@ -1779,6 +1887,14 @@
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"k": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.7320508075688772
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1805,7 +1921,7 @@
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -1884,17 +2000,45 @@
|
||||
"df": 2,
|
||||
"docs": {
|
||||
"4": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"o": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"p": {
|
||||
"a": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"g": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"e": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"r": {
|
||||
@@ -1967,8 +2111,12 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 2.0
|
||||
}
|
||||
},
|
||||
"l": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
@@ -2031,7 +2179,7 @@
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -2063,7 +2211,15 @@
|
||||
}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
"docs": {},
|
||||
"l": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"df": 0,
|
||||
@@ -2122,13 +2278,16 @@
|
||||
"docs": {},
|
||||
"n": {
|
||||
"d": {
|
||||
"df": 2,
|
||||
"df": 3,
|
||||
"docs": {
|
||||
"7": {
|
||||
"tf": 1.0
|
||||
},
|
||||
"8": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2146,13 +2305,16 @@
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 2,
|
||||
"df": 3,
|
||||
"docs": {
|
||||
"3": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"5": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
},
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2216,7 +2378,7 @@
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"7": {
|
||||
"tf": 1.4142135623730952
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2266,8 +2428,12 @@
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {}
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.4142135623730951
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x": {
|
||||
@@ -2296,6 +2462,14 @@
|
||||
"r": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"k": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"l": {
|
||||
"d": {
|
||||
"df": 1,
|
||||
@@ -2388,7 +2562,7 @@
|
||||
"s": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"10": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
@@ -2511,6 +2685,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"l": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"i": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"k": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"n": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
@@ -2531,6 +2725,62 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"p": {
|
||||
"a": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"g": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"e": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"r": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"i": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"n": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"r": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"e": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"l": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"s": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
@@ -2609,6 +2859,26 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"t": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"e": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"s": {
|
||||
"df": 0,
|
||||
"docs": {},
|
||||
"t": {
|
||||
"df": 1,
|
||||
"docs": {
|
||||
"9": {
|
||||
"tf": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user