Compare commits

..

2 Commits

Author SHA1 Message Date
Eric Huss
f66df09324 Bump 0.4.5 2021-01-04 07:03:20 -08:00
Pietro Albini
648c9ae772 fix xss in the search page
Thanks to Kamil Vavra for responsibly disclosing the vulnerability
according to Rust's Security Policy.
2021-01-04 07:01:49 -08:00
44 changed files with 262 additions and 845 deletions

View File

@@ -1,81 +1,5 @@
# Changelog
## mdBook 0.4.8
[fcceee4...b592b10](https://github.com/rust-lang/mdBook/compare/fcceee4...b592b10)
### Added
- Added the option `output.html.edit-url-template` which can be a URL which is
linked on each page to direct the user to a site (such as GitHub) where the
user can directly suggest an edit for the page they are currently reading.
[#1506](https://github.com/rust-lang/mdBook/pull/1506)
### Changed
- Printed output now includes a page break between chapters.
[#1485](https://github.com/rust-lang/mdBook/pull/1485)
### Fixed
- HTML, such as HTML comments, is now ignored if it appears above the title line
in `SUMMARY.md`.
[#1437](https://github.com/rust-lang/mdBook/pull/1437)
## mdBook 0.4.7
[9a9eb01...c83bbd6](https://github.com/rust-lang/mdBook/compare/9a9eb01...c83bbd6)
### Changed
- Updated shlex parser to fix a minor parsing issue (used by the
preprocessor/backend custom command config).
[#1471](https://github.com/rust-lang/mdBook/pull/1471)
- Enhanced text contrast of `light` theme to improve accessibility.
[#1470](https://github.com/rust-lang/mdBook/pull/1470)
### Fixed
- Fixed some issues with fragment scrolling and linking.
[#1463](https://github.com/rust-lang/mdBook/pull/1463)
## mdBook 0.4.6
[eaa6914...1a0c296](https://github.com/rust-lang/mdBook/compare/eaa6914...1a0c296)
### Changed
- The chapter name is now included in the search breadcrumbs.
[#1389](https://github.com/rust-lang/mdBook/pull/1389)
- Pressing Escape will remove the `?highlight` argument from the URL.
[#1427](https://github.com/rust-lang/mdBook/pull/1427)
- `mdbook init --theme` will now place the theme in the root of the book
directory instead of in the `src` directory.
[#1432](https://github.com/rust-lang/mdBook/pull/1432)
- A custom renderer that sets the `command` to a relative path now interprets
the relative path relative to the book root. Previously it was inconsistent
based on the platform (either relative to the current directory, or relative
to the renderer output directory). Paths relative to the output directory
are still supported with a deprecation warning.
[#1418](https://github.com/rust-lang/mdBook/pull/1418)
- The `theme` directory in the config is now interpreted as relative to the
book root, instead of the current directory.
[#1405](https://github.com/rust-lang/mdBook/pull/1405)
- Handle UTF-8 BOM for chapter sources.
[#1285](https://github.com/rust-lang/mdBook/pull/1285)
- Removed extra whitespace added to `{{#playground}}` snippets.
[#1375](https://github.com/rust-lang/mdBook/pull/1375)
### Fixed
- Clicking on a search result with multiple search words will now correctly
highlight all of the words.
[#1426](https://github.com/rust-lang/mdBook/pull/1426)
- Properly handle `<` and `>` characters in the table of contents.
[#1376](https://github.com/rust-lang/mdBook/pull/1376)
- Fixed to properly serialize the `build` table in the config, which prevented
setting it in the API.
[#1378](https://github.com/rust-lang/mdBook/pull/1378)
## mdBook 0.4.5
[eaa6914...f66df09](https://github.com/rust-lang/mdBook/compare/eaa6914...f66df09)
### Fixed
- Fixed XSS in the search page.
[CVE-2020-26297](https://groups.google.com/g/rustlang-security-announcements/c/3-sO6of29O0)
[648c9ae](https://github.com/rust-lang/mdBook/commit/648c9ae772bec83f0a5954d17b4287d5bb1d6606)
## mdBook 0.4.4
[4df9ec9...01836ba](https://github.com/rust-lang/mdBook/compare/4df9ec9...01836ba)

6
Cargo.lock generated
View File

@@ -723,7 +723,7 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "mdbook"
version = "0.4.8"
version = "0.4.5"
dependencies = [
"ammonia",
"anyhow",
@@ -1258,9 +1258,9 @@ dependencies = [
[[package]]
name = "shlex"
version = "1.0.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]]
name = "siphasher"

View File

@@ -1,6 +1,6 @@
[package]
name = "mdbook"
version = "0.4.8"
version = "0.4.5"
authors = [
"Mathieu David <mathieudavid@mathieudavid.org>",
"Michael-F-Bryan <michaelfbryan@gmail.com>",
@@ -30,7 +30,7 @@ regex = "1.0.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
shlex = "1"
shlex = "0.1"
tempfile = "3.0"
toml = "0.5.1"

View File

@@ -91,11 +91,10 @@ all its functionality as a Rust crate for integration in other projects.
Here are the main commands you will want to run. For a more exhaustive
explanation, check out the [User Guide].
- `mdbook init <directory>`
- `mdbook init`
The init command will create a directory with the minimal boilerplate to
start with. If the `<directory>` parameter is omitted, the current
directory will be used.
start with.
```
book-test/
@@ -150,7 +149,6 @@ preprocessors are:
the url `foo/` when published to a browser
- `links` - a built-in preprocessor (enabled by default) for expanding the
`{{# playground}}` and `{{# include}}` helpers in a chapter.
- [`katex`](https://github.com/lzanini/mdbook-katex) - a preprocessor rendering LaTex equations to HTML.
Renderers are given the final book so they can do something with it. This is
typically used for, as the name suggests, rendering the document in a particular

View File

@@ -10,8 +10,6 @@ edition = "2018"
[output.html]
mathjax-support = true
site-url = "/mdBook/"
git-repository-url = "https://github.com/rust-lang/mdBook/tree/master/guide"
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
[output.html.playground]
editable = true

View File

@@ -12,7 +12,7 @@ to download the appropriate version for your platform.
## Install From Source
mdBook can also be installed by compiling the source code on your local machine.
mdBook can also be installed from source
### Pre-requisite

View File

@@ -25,9 +25,9 @@ book-test/
- The `book` directory is where your book is rendered. All the output is ready
to be uploaded to a server to be seen by your audience.
- The `SUMMARY.md` is the skeleton of your
book, and is discussed in more detail [in another
chapter](../format/summary.md).
- The `SUMMARY.md` file is the most important file, it's the skeleton of your
book and is discussed in more detail [in another
chapter](../format/summary.md)
#### Tip: Generate chapters from SUMMARY.md

View File

@@ -1,13 +1,7 @@
# The serve command
The serve command is used to preview a book by serving it via HTTP at
`localhost:3000` by default:
```bash
mdbook serve
```
The `serve` command watches the book's `src` directory for
The serve command is used to preview a book by serving it over HTTP at
`localhost:3000` by default. Additionally it watches the book's directory for
changes, rebuilding the book and refreshing clients for each change. A websocket
connection is used to trigger the client-side refresh.
@@ -23,14 +17,24 @@ root instead of the current working directory.
mdbook serve path/to/book
```
### Server options
#### Server options
The `serve` hostname defaults to `localhost`, and the port defaults to `3000`. Either option can be specified on the command line:
`serve` has four options: the HTTP port, the WebSocket port, the HTTP hostname
to listen on, and the hostname for the browser to connect to for WebSockets.
For example: suppose you have an nginx server for SSL termination which has a
public address of 192.168.1.100 on port 80 and proxied that to 127.0.0.1 on port
8000\. To run use the nginx proxy do:
```bash
mdbook serve path/to/book -p 8000 -n 127.0.0.1
mdbook serve path/to/book -p 8000 -n 127.0.0.1 --websocket-hostname 192.168.1.100
```
If you were to want live reloading for this you would need to proxy the
websocket calls through nginx as well from `192.168.1.100:<WS_PORT>` to
`127.0.0.1:<WS_PORT>`. The `-w` flag allows for the websocket port to be
configured.
#### --open
When you use the `--open` (`-o`) flag, mdbook will open the book in your
@@ -51,5 +55,5 @@ contain file patterns described in the [gitignore
documentation](https://git-scm.com/docs/gitignore). This can be useful for
ignoring temporary files created by some editors.
***Note:*** *Only the `.gitignore` from the book root directory is used. Global
`$HOME/.gitignore` or `.gitignore` files in parent directories are not used.*
_Note: Only `.gitignore` from book root directory is used. Global
`$HOME/.gitignore` or `.gitignore` files in parent directories are not used._

View File

@@ -39,9 +39,6 @@ permissions (or "repo" for private repositories). Go to your repository's Travis
CI settings page and add an environment variable named `GITHUB_TOKEN` that is
marked secure and *not* shown in the logs.
Whilst still in your repository's settings page, navigate to Options and change the
Source on GitHub pages to `gh-pages`.
Then, append this snippet to your `.travis.yml` and update the path to the
`book` directory:
@@ -58,40 +55,6 @@ deploy:
That's it!
Note: Travis has a new [dplv2](https://blog.travis-ci.com/2019-08-27-deployment-tooling-dpl-v2-preview-release) configuration that is currently in beta. To use this new format, update your `.travis.yml` file to:
```yaml
language: rust
os: linux
dist: xenial
cache:
- cargo
rust:
- stable
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.3" mdbook)
- cargo install-update -a
script:
- mdbook build path/to/mybook && mdbook test path/to/mybook
deploy:
provider: pages
strategy: git
edge: true
cleanup: false
github-token: $GITHUB_TOKEN
local-dir: path/to/mybook/book
keep-history: false
on:
branch: master
target_branch: gh-pages
```
### Deploying to GitHub Pages manually
If your CI doesn't support GitHub pages, or you're deploying somewhere else
@@ -124,31 +87,3 @@ deploy: book
git commit -m "deployed on $(shell date) by ${USER}" && \
git push origin gh-pages
```
## Deploying Your Book to GitLab Pages
Inside your repository's project root, create a file named `.gitlab-ci.yml` with the following contents:
```yml
stages:
- deploy
pages:
stage: deploy
image: rust
variables:
CARGO_HOME: $CI_PROJECT_DIR/cargo
before_script:
- export PATH="$PATH:$CARGO_HOME/bin"
- mdbook --version || cargo install mdbook
script:
- mdbook build -d public
only:
- master
artifacts:
paths:
- public
cache:
paths:
- $CARGO_HOME/bin
```
After you commit and push this new file, GitLab CI will run and your book will be available!

View File

@@ -18,9 +18,9 @@ A new table is added to `book.toml` (e.g. `preprocessor.foo` for the `foo`
preprocessor) and then `mdbook` will try to invoke the `mdbook-foo` program as
part of the build process.
A preprocessor can be hard-coded to specify which backend(s) it should be run
for with the `preprocessor.foo.renderer` key. For example, it doesn't make sense for
[MathJax](../format/mathjax.md) to be used for non-HTML renderers.
While preprocessors can be hard-coded to specify which backend it should be run
for (e.g. it doesn't make sense for MathJax to be used for non-HTML renderers)
with the `preprocessor.foo.renderer` key.
```toml
[book]

View File

@@ -201,14 +201,6 @@ The following configuration options are available:
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`.
- **edit-url-template:** Edit url template, when provided shows a
"Suggest an edit" button for directly jumping to editing the currently
viewed page. For e.g. GitHub projects set this to
`https://github.com/<owner>/<repo>/edit/master/{path}` or for
Bitbucket projects set it to
`https://bitbucket.org/<owner>/<repo>/src/master/{path}?mode=edit`
where {path} will be replaced with the full path of the file in the
repository.
- **redirect:** A subtable used for generating redirects when a page is moved.
The table contains key-value pairs where the key is where the redirect file
needs to be created, as an absolute path from the build directory, (e.g.
@@ -294,7 +286,6 @@ additional-js = ["custom.js"]
no-section-label = false
git-repository-url = "https://github.com/rust-lang/mdBook"
git-repository-icon = "fa-github"
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
site-url = "/example-book/"
cname = "myproject.rs"
input-404 = "not-found.md"

View File

@@ -1,6 +1,6 @@
fn main() {
println!("Hello World!");
#
# // You can even hide lines! :D
# println!("I am hidden! Expand the code snippet to see me");
# // You can even hide lines! :D
# println!("I am hidden! Expand the code snippet to see me");
}

View File

@@ -40,7 +40,7 @@ The path to the file has to be relative from the current source file.
mdBook will interpret included files as Markdown. Since the include command
is usually used for inserting code snippets and examples, you will often
wrap the command with ```` ``` ```` to display the file contents without
interpreting them.
interpretting them.
````hbs
```
@@ -49,7 +49,7 @@ interpreting them.
````
## Including portions of a file
Often you only need a specific part of the file, e.g. relevant lines for an
Often you only need a specific part of the file e.g. relevant lines for an
example. We support four different modes of partial includes:
```hbs
@@ -68,8 +68,8 @@ consisting of lines 2 to 10.
To avoid breaking your book when modifying included files, you can also
include a specific section using anchors instead of line numbers.
An anchor is a pair of matching lines. The line beginning an anchor must
match the regex `ANCHOR:\s*[\w_-]+` and similarly the ending line must match
the regex `ANCHOR_END:\s*[\w_-]+`. This allows you to put anchors in
match the regex "ANCHOR:\s*[\w_-]+" and similarly the ending line must match
the regex "ANCHOR_END:\s*[\w_-]+". This allows you to put anchors in
any kind of commented line.
Consider the following file to include:
@@ -156,7 +156,7 @@ To call the `add_one` function, we pass it an `i32` and bind the returned value
#
# fn add_one(num: i32) -> i32 {
# num + 1
# }
#}
```
````
@@ -170,7 +170,7 @@ That is, it looks like this (click the "expand" icon to see the rest of the file
#
# fn add_one(num: i32) -> i32 {
# num + 1
# }
#}
```
## Inserting runnable Rust files
@@ -192,12 +192,3 @@ Here is what a rendered code snippet looks like:
{{#playground example.rs}}
[Rust Playground]: https://play.rust-lang.org/
## Controlling page \<title\>
A chapter can set a \<title\> that is different from its entry in the table of
contents (sidebar) by including a `\{{#title ...}}` near the top of the page.
```hbs
\{{#title My Title}}
```

View File

@@ -34,14 +34,11 @@ allow for easy parsing. Let's see how you should format your `SUMMARY.md` file.
```markdown
# Title of Part
- [Title of the first Chapter](relative/path/to/markdown.md)
- [Title of the second Chapter](relative/path/to/markdown2.md)
- [Title of a sub Chapter](relative/path/to/markdown3.md)
- [Title of the Chapter](relative/path/to/markdown.md)
# Title of Another Part
- [More Chapters](relative/path/to/markdown4.md)
- [More Chapters](relative/path/to/markdown2.md)
```
You can either use `-` or `*` to indicate a numbered chapter.

View File

@@ -14,11 +14,9 @@ Here are the files you can override:
- **_index.hbs_** is the handlebars template.
- **_head.hbs_** is appended to the HTML `<head>` section.
- **_header.hbs_** content is appended on top of every book page.
- **_css/_** contains the CSS files for styling the book.
- **_css/chrome.css_** is for UI elements.
- **_css/general.css_** is the base styles.
- **_css/print.css_** is the style for printer output.
- **_css/variables.css_** contains variables used in other CSS files.
- **_book.css_** is the style used in the output. If you want to change the
design of your book, this is probably the file you want to modify. Sometimes
in conjunction with `index.hbs` when you want to radically change the layout.
- **_book.js_** is mostly used to add client side functionality, like hiding /
un-hiding the sidebar, changing the theme, ...
- **_highlight.js_** is the JavaScript that is used to highlight code snippets,

View File

@@ -19,7 +19,7 @@ Here is a list of the properties that are exposed:
- ***language*** Language of the book in the form `en`, as specified in `book.toml` (if not specified, defaults to `en`). To use in <code
class="language-html">\<html lang="{{ language }}"></code> for example.
- ***title*** Title used for the current page. This is identical to `{{ chapter_title }} - {{ book_title }}` unless `book_title` is not set in which case it just defaults to the `chapter_title`.
- ***title*** Title used for the current page. This is identical to `{{ book_title }} - {{ chapter_title }}` unless `book_title` is not set in which case it just defaults to the `chapter_title`.
- ***book_title*** Title of the book, as specified in `book.toml`
- ***chapter_title*** Title of the current chapter, as listed in `SUMMARY.md`

View File

@@ -112,10 +112,10 @@ everyone can benefit from it.**
## Improve default theme
If you think the default theme doesn't look quite right for a specific language,
or could be improved, feel free to [submit a new
or could be improved. Feel free to [submit a new
issue](https://github.com/rust-lang/mdBook/issues) explaining what you
have in mind and I will take a look at it.
You could also create a pull-request with the proposed improvements.
Overall the theme should be light and sober, without too many flashy colors.
Overall the theme should be light and sober, without to many flashy colors.

View File

@@ -16,7 +16,5 @@ shout-out to them!
- [Phaiax](https://github.com/Phaiax)
- Matt Ickstadt ([mattico](https://github.com/mattico))
- Weihang Lo ([@weihanglo](https://github.com/weihanglo))
- Avision Ho ([@avisionh](https://github.com/avisionh))
- Vivek Akupatni ([@apatniv](https://github.com/apatniv))
If you feel you're missing from this list, feel free to add yourself in a PR.
If you feel you're missing from this list, feel free to add yourself in a PR.

View File

@@ -14,12 +14,11 @@ pub fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book>
let summary_md = src_dir.join("SUMMARY.md");
let mut summary_content = String::new();
File::open(&summary_md)
.with_context(|| format!("Couldn't open SUMMARY.md in {:?} directory", src_dir))?
File::open(summary_md)
.with_context(|| "Couldn't open SUMMARY.md")?
.read_to_string(&mut summary_content)?;
let summary = parse_summary(&summary_content)
.with_context(|| format!("Summary parsing failed for file={:?}", summary_md))?;
let summary = parse_summary(&summary_content).with_context(|| "Summary parsing failed")?;
if cfg.create_missing {
create_missing(&src_dir, &summary).with_context(|| "Unable to create missing chapters")?;
@@ -50,9 +49,7 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
}
debug!("Creating missing file {}", filename.display());
let mut f = File::create(&filename).with_context(|| {
format!("Unable to create missing file: {}", filename.display())
})?;
let mut f = File::create(&filename)?;
writeln!(f, "# {}", link.name)?;
}
}
@@ -66,7 +63,7 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
/// A dumb tree structure representing a book.
///
/// For the moment a book is just a collection of [`BookItems`] which are
/// For the moment a book is just a collection of `BookItems` which are
/// accessible by either iterating (immutably) over the book with [`iter()`], or
/// recursively applying a closure to each section to mutate the chapters, using
/// [`for_each_mut()`].
@@ -160,9 +157,7 @@ pub struct Chapter {
pub sub_items: Vec<BookItem>,
/// The chapter's location, relative to the `SUMMARY.md` file.
pub path: Option<PathBuf>,
/// The chapter's source file, relative to the `SUMMARY.md` file.
pub source_path: Option<PathBuf>,
/// An ordered list of the names of each chapter above this one in the hierarchy.
/// An ordered list of the names of each chapter above this one, in the hierarchy.
pub parent_names: Vec<String>,
}
@@ -171,34 +166,31 @@ impl Chapter {
pub fn new<P: Into<PathBuf>>(
name: &str,
content: String,
p: P,
path: P,
parent_names: Vec<String>,
) -> Chapter {
let path: PathBuf = p.into();
Chapter {
name: name.to_string(),
content,
path: Some(path.clone()),
source_path: Some(path),
path: Some(path.into()),
parent_names,
..Default::default()
}
}
/// Create a new draft chapter that is not attached to a source markdown file (and thus
/// has no content).
/// Create a new draft chapter that is not attached to a source markdown file and has
/// thus no content.
pub fn new_draft(name: &str, parent_names: Vec<String>) -> Self {
Chapter {
name: name.to_string(),
content: String::new(),
path: None,
source_path: None,
parent_names,
..Default::default()
}
}
/// Check if the chapter is a draft chapter, meaning it has no path to a source markdown file.
/// Check if the chapter is a draft chapter, meaning it has no path to a source markdown file
pub fn is_draft_chapter(&self) -> bool {
match self.path {
Some(_) => false,
@@ -272,10 +264,6 @@ fn load_chapter<P: AsRef<Path>>(
format!("Unable to read \"{}\" ({})", link.name, location.display())
})?;
if content.as_bytes().starts_with(b"\xef\xbb\xbf") {
content.replace_range(..3, "");
}
let stripped = location
.strip_prefix(&src_dir)
.expect("Chapters are always inside a book");
@@ -285,7 +273,7 @@ fn load_chapter<P: AsRef<Path>>(
Chapter::new_draft(&link.name, parent_names.clone())
};
let mut sub_item_parents = parent_names;
let mut sub_item_parents = parent_names.clone();
ch.number = link.number.clone();
@@ -307,6 +295,8 @@ fn load_chapter<P: AsRef<Path>>(
///
/// This struct shouldn't be created directly, instead prefer the
/// [`Book::iter()`] method.
///
/// [`Book::iter()`]: struct.Book.html#method.iter
pub struct BookItems<'a> {
items: VecDeque<&'a BookItem>,
}
@@ -403,29 +393,6 @@ And here is some \
assert_eq!(got, should_be);
}
#[test]
fn load_a_single_chapter_with_utf8_bom_from_disk() {
let temp_dir = TempFileBuilder::new().prefix("book").tempdir().unwrap();
let chapter_path = temp_dir.path().join("chapter_1.md");
File::create(&chapter_path)
.unwrap()
.write_all(("\u{feff}".to_owned() + DUMMY_SRC).as_bytes())
.unwrap();
let link = Link::new("Chapter 1", chapter_path);
let should_be = Chapter::new(
"Chapter 1",
DUMMY_SRC.to_string(),
"chapter_1.md",
Vec::new(),
);
let got = load_chapter(&link, temp_dir.path(), Vec::new()).unwrap();
assert_eq!(got, should_be);
}
#[test]
fn cant_load_a_nonexistent_chapter() {
let link = Link::new("Chapter 1", "/foo/bar/baz.md");
@@ -443,7 +410,6 @@ And here is some \
content: String::from("Hello World!"),
number: Some(SectionNumber(vec![1, 2])),
path: Some(PathBuf::from("second.md")),
source_path: Some(PathBuf::from("second.md")),
parent_names: vec![String::from("Chapter 1")],
sub_items: Vec::new(),
};
@@ -452,7 +418,6 @@ And here is some \
content: String::from(DUMMY_SRC),
number: None,
path: Some(PathBuf::from("chapter_1.md")),
source_path: Some(PathBuf::from("chapter_1.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(nested.clone()),
@@ -477,7 +442,6 @@ And here is some \
name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC),
path: Some(PathBuf::from("chapter_1.md")),
source_path: Some(PathBuf::from("chapter_1.md")),
..Default::default()
})],
..Default::default()
@@ -518,7 +482,6 @@ And here is some \
content: String::from(DUMMY_SRC),
number: None,
path: Some(PathBuf::from("Chapter_1/index.md")),
source_path: Some(PathBuf::from("Chapter_1/index.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(Chapter::new(
@@ -571,7 +534,6 @@ And here is some \
content: String::from(DUMMY_SRC),
number: None,
path: Some(PathBuf::from("Chapter_1/index.md")),
source_path: Some(PathBuf::from("Chapter_1/index.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(Chapter::new(

View File

@@ -28,7 +28,7 @@ impl BookBuilder {
}
}
/// Set the [`Config`] to be used.
/// Set the `Config` to be used.
pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder {
self.config = cfg;
self
@@ -110,7 +110,10 @@ impl BookBuilder {
debug!("Copying theme");
let html_config = self.config.html_config().unwrap_or_default();
let themedir = html_config.theme_dir(&self.root);
let themedir = html_config
.theme
.unwrap_or_else(|| self.config.book.src.join("theme"));
let themedir = self.root.join(themedir);
if !themedir.exists() {
debug!(
@@ -124,9 +127,7 @@ impl BookBuilder {
index.write_all(theme::INDEX)?;
let cssdir = themedir.join("css");
if !cssdir.exists() {
fs::create_dir(&cssdir)?;
}
fs::create_dir(&cssdir)?;
let mut general_css = File::create(cssdir.join("general.css"))?;
general_css.write_all(theme::GENERAL_CSS)?;

View File

@@ -40,7 +40,7 @@ pub struct MDBook {
pub book: Book,
renderers: Vec<Box<dyn Renderer>>,
/// List of pre-processors to be run on the book.
/// List of pre-processors to be run on the book
preprocessors: Vec<Box<dyn Preprocessor>>,
}
@@ -78,7 +78,7 @@ impl MDBook {
MDBook::load_with_config(book_root, config)
}
/// Load a book from its root directory using a custom `Config`.
/// Load a book from its root directory using a custom config.
pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
let root = book_root.into();
@@ -97,7 +97,7 @@ impl MDBook {
})
}
/// Load a book from its root directory using a custom `Config` and a custom summary.
/// Load a book from its root directory using a custom config and a custom summary.
pub fn load_with_config_and_summary<P: Into<PathBuf>>(
book_root: P,
config: Config,
@@ -121,7 +121,7 @@ impl MDBook {
}
/// Returns a flat depth-first iterator over the elements of the book,
/// it returns a [`BookItem`] enum:
/// it returns an [BookItem enum](bookitem.html):
/// `(section: String, bookitem: &BookItem)`
///
/// ```no_run
@@ -180,7 +180,7 @@ impl MDBook {
Ok(())
}
/// Run the entire build process for a particular [`Renderer`].
/// Run the entire build process for a particular `Renderer`.
pub fn execute_build_process(&self, renderer: &dyn Renderer) -> Result<()> {
let mut preprocessed_book = self.book.clone();
let preprocess_ctx = PreprocessorContext::new(
@@ -196,34 +196,37 @@ impl MDBook {
}
}
info!("Running the {} backend", renderer.name());
self.render(&preprocessed_book, renderer)?;
Ok(())
}
fn render(&self, preprocessed_book: &Book, renderer: &dyn Renderer) -> Result<()> {
let name = renderer.name();
let build_dir = self.build_dir_for(name);
let mut render_context = RenderContext::new(
let render_context = RenderContext::new(
self.root.clone(),
preprocessed_book,
preprocessed_book.clone(),
self.config.clone(),
build_dir,
);
render_context
.chapter_titles
.extend(preprocess_ctx.chapter_titles.borrow_mut().drain());
info!("Running the {} backend", renderer.name());
renderer
.render(&render_context)
.with_context(|| "Rendering failed")
}
/// You can change the default renderer to another one by using this method.
/// The only requirement is that your renderer implement the [`Renderer`]
/// trait.
/// The only requirement is for your renderer to implement the [`Renderer`
/// trait](../renderer/trait.Renderer.html)
pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {
self.renderers.push(Box::new(renderer));
self
}
/// Register a [`Preprocessor`] to be used when rendering the book.
/// Register a [`Preprocessor`](../preprocess/trait.Preprocessor.html) to be used when rendering the book.
pub fn with_preprocessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {
self.preprocessors.push(Box::new(preprocessor));
self
@@ -300,7 +303,7 @@ impl MDBook {
/// artefacts.
///
/// If there is only 1 renderer, put it in the directory pointed to by the
/// `build.build_dir` key in [`Config`]. If there is more than one then the
/// `build.build_dir` key in `Config`. If there is more than one then the
/// renderer gets its own directory within the main build dir.
///
/// i.e. If there were only one renderer (in this case, the HTML renderer):

View File

@@ -525,19 +525,14 @@ impl<'a> SummaryParser<'a> {
/// Try to parse the title line.
fn parse_title(&mut self) -> Option<String> {
loop {
match self.next_event() {
Some(Event::Start(Tag::Heading(1))) => {
debug!("Found a h1 in the SUMMARY");
match self.next_event() {
Some(Event::Start(Tag::Heading(1))) => {
debug!("Found a h1 in the SUMMARY");
let tags = collect_events!(self.stream, end Tag::Heading(1));
return Some(stringify_events(tags));
}
// Skip a HTML element such as a comment line.
Some(Event::Html(_)) => {}
// Otherwise, no title.
_ => return None,
let tags = collect_events!(self.stream, end Tag::Heading(1));
Some(stringify_events(tags))
}
_ => None,
}
}
}
@@ -978,103 +973,4 @@ mod tests {
assert_eq!(got, should_be);
}
#[test]
fn skip_html_comments() {
let src = r#"<!--
# Title - En
-->
# Title - Local
<!--
[Prefix 00-01 - En](ch00-01.md)
[Prefix 00-02 - En](ch00-02.md)
-->
[Prefix 00-01 - Local](ch00-01.md)
[Prefix 00-02 - Local](ch00-02.md)
<!--
## Section Title - En
-->
## Section Title - Localized
<!--
- [Ch 01-00 - En](ch01-00.md)
- [Ch 01-01 - En](ch01-01.md)
- [Ch 01-02 - En](ch01-02.md)
-->
- [Ch 01-00 - Local](ch01-00.md)
- [Ch 01-01 - Local](ch01-01.md)
- [Ch 01-02 - Local](ch01-02.md)
<!--
- [Ch 02-00 - En](ch02-00.md)
-->
- [Ch 02-00 - Local](ch02-00.md)
<!--
[Appendix A - En](appendix-01.md)
[Appendix B - En](appendix-02.md)
-->`
[Appendix A - Local](appendix-01.md)
[Appendix B - Local](appendix-02.md)
"#;
let mut parser = SummaryParser::new(src);
// ---- Title ----
let title = parser.parse_title();
assert_eq!(title, Some(String::from("Title - Local")));
// ---- Prefix Chapters ----
let new_affix_item = |name, location| {
SummaryItem::Link(Link {
name: String::from(name),
location: Some(PathBuf::from(location)),
..Default::default()
})
};
let should_be = vec![
new_affix_item("Prefix 00-01 - Local", "ch00-01.md"),
new_affix_item("Prefix 00-02 - Local", "ch00-02.md"),
];
let got = parser.parse_affix(true).unwrap();
assert_eq!(got, should_be);
// ---- Numbered Chapters ----
let new_numbered_item = |name, location, numbers: &[u32], nested_items| {
SummaryItem::Link(Link {
name: String::from(name),
location: Some(PathBuf::from(location)),
number: Some(SectionNumber(numbers.to_vec())),
nested_items,
})
};
let ch01_nested = vec![
new_numbered_item("Ch 01-01 - Local", "ch01-01.md", &[1, 1], vec![]),
new_numbered_item("Ch 01-02 - Local", "ch01-02.md", &[1, 2], vec![]),
];
let should_be = vec![
new_numbered_item("Ch 01-00 - Local", "ch01-00.md", &[1], ch01_nested),
new_numbered_item("Ch 02-00 - Local", "ch02-00.md", &[2], vec![]),
];
let got = parser.parse_parts().unwrap();
assert_eq!(got, should_be);
// ---- Suffix Chapters ----
let should_be = vec![
new_affix_item("Appendix A - Local", "appendix-01.md"),
new_affix_item("Appendix B - Local", "appendix-02.md"),
];
let got = parser.parse_affix(false).unwrap();
assert_eq!(got, should_be);
}
}

View File

@@ -28,11 +28,15 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
// If flag `--theme` is present, copy theme to src
if args.is_present("theme") {
let theme_dir = book_dir.join("theme");
println!();
println!("Copying the default theme to {}", theme_dir.display());
config.set("output.html.theme", "src/theme")?;
// Skip this if `--force` is present
if !args.is_present("force") && theme_dir.exists() {
if !args.is_present("force") {
// Print warning
println!();
println!(
"Copying the default theme to {}",
builder.config().book.src.display()
);
println!("This could potentially overwrite files already present in that directory.");
print!("\nAre you sure you want to continue? (y/n) ");

View File

@@ -2,7 +2,7 @@
//!
//! The main entrypoint of the `config` module is the `Config` struct. This acts
//! essentially as a bag of configuration information, with a couple
//! pre-determined tables ([`BookConfig`] and [`BuildConfig`]) as well as support
//! pre-determined tables (`BookConfig` and `BuildConfig`) as well as support
//! for arbitrary data which is exposed to plugins and alternative backends.
//!
//!
@@ -352,11 +352,6 @@ impl Serialize for Config {
let book_config = Value::try_from(&self.book).expect("should always be serializable");
table.insert("book", book_config);
if self.build != BuildConfig::default() {
let build_config = Value::try_from(&self.build).expect("should always be serializable");
table.insert("build", build_config);
}
if self.rust != RustConfig::default() {
let rust_config = Value::try_from(&self.rust).expect("should always be serializable");
table.insert("rust", rust_config);
@@ -522,10 +517,6 @@ pub struct HtmlConfig {
///
/// [custom domain]: https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
pub cname: Option<String>,
/// Edit url template, when set shows a "Suggest an edit" button for
/// directly jumping to editing the currently viewed page.
/// Contains {path} that is replaced with chapter source file path
pub edit_url_template: Option<String>,
/// This is used as a bit of a workaround for the `mdbook serve` command.
/// Basically, because you set the websocket port from the command line, the
/// `mdbook serve` command needs a way to let the HTML renderer know where
@@ -558,7 +549,6 @@ impl Default for HtmlConfig {
search: None,
git_repository_url: None,
git_repository_icon: None,
edit_url_template: None,
input_404: None,
site_url: None,
cname: None,

View File

@@ -76,9 +76,9 @@
//! access to the various methods for working with the [`Config`].
//!
//! [user guide]: https://rust-lang.github.io/mdBook/
//! [`RenderContext`]: renderer::RenderContext
//! [`RenderContext`]: renderer/struct.RenderContext.html
//! [relevant chapter]: https://rust-lang.github.io/mdBook/for_developers/backends.html
//! [`Config`]: config::Config
//! [`Config`]: config/struct.Config.html
#![deny(missing_docs)]
#![deny(rust_2018_idioms)]

View File

@@ -23,7 +23,6 @@ const MAX_LINK_NESTED_DEPTH: usize = 10;
/// This hides the lines from initial display but shows them when the reader expands the code
/// block and provides them to Rustdoc for testing.
/// - `{{# playground}}` - Insert runnable Rust files
/// - `{{# title}}` - Override \<title\> of a webpage.
#[derive(Default)]
pub struct LinkPreprocessor;
@@ -52,15 +51,8 @@ impl Preprocessor for LinkPreprocessor {
.map(|dir| src_dir.join(dir))
.expect("All book items have a parent");
let mut chapter_title = ch.name.clone();
let content =
replace_all(&ch.content, base, chapter_path, 0, &mut chapter_title);
let content = replace_all(&ch.content, base, chapter_path, 0);
ch.content = content;
if chapter_title != ch.name {
ctx.chapter_titles
.borrow_mut()
.insert(chapter_path.clone(), chapter_title);
}
}
}
});
@@ -69,13 +61,7 @@ impl Preprocessor for LinkPreprocessor {
}
}
fn replace_all<P1, P2>(
s: &str,
path: P1,
source: P2,
depth: usize,
chapter_title: &mut String,
) -> String
fn replace_all<P1, P2>(s: &str, path: P1, source: P2, depth: usize) -> String
where
P1: AsRef<Path>,
P2: AsRef<Path>,
@@ -91,17 +77,11 @@ where
for link in find_links(s) {
replaced.push_str(&s[previous_end_index..link.start_index]);
match link.render_with_path(&path, chapter_title) {
match link.render_with_path(&path) {
Ok(new_content) => {
if depth < MAX_LINK_NESTED_DEPTH {
if let Some(rel_path) = link.link_type.relative_path(path) {
replaced.push_str(&replace_all(
&new_content,
rel_path,
source,
depth + 1,
chapter_title,
));
replaced.push_str(&replace_all(&new_content, rel_path, source, depth + 1));
} else {
replaced.push_str(&new_content);
}
@@ -136,7 +116,6 @@ enum LinkType<'a> {
Include(PathBuf, RangeOrAnchor),
Playground(PathBuf, Vec<&'a str>),
RustdocInclude(PathBuf, RangeOrAnchor),
Title(&'a str),
}
#[derive(PartialEq, Debug, Clone)]
@@ -206,7 +185,6 @@ impl<'a> LinkType<'a> {
LinkType::Include(p, _) => Some(return_relative_path(base, &p)),
LinkType::Playground(p, _) => Some(return_relative_path(base, &p)),
LinkType::RustdocInclude(p, _) => Some(return_relative_path(base, &p)),
LinkType::Title(_) => None,
}
}
}
@@ -277,9 +255,6 @@ struct Link<'a> {
impl<'a> Link<'a> {
fn from_capture(cap: Captures<'a>) -> Option<Link<'a>> {
let link_type = match (cap.get(0), cap.get(1), cap.get(2)) {
(_, Some(typ), Some(title)) if typ.as_str() == "title" => {
Some(LinkType::Title(title.as_str()))
}
(_, Some(typ), Some(rest)) => {
let mut path_props = rest.as_str().split_whitespace();
let file_arg = path_props.next();
@@ -316,11 +291,7 @@ impl<'a> Link<'a> {
})
}
fn render_with_path<P: AsRef<Path>>(
&self,
base: P,
chapter_title: &mut String,
) -> Result<String> {
fn render_with_path<P: AsRef<Path>>(&self, base: P) -> Result<String> {
let base = base.as_ref();
match self.link_type {
// omit the escape char
@@ -364,7 +335,7 @@ impl<'a> Link<'a> {
LinkType::Playground(ref pat, ref attrs) => {
let target = base.join(pat);
let mut contents = fs::read_to_string(&target).with_context(|| {
let contents = fs::read_to_string(&target).with_context(|| {
format!(
"Could not read file for link {} ({})",
self.link_text,
@@ -372,20 +343,13 @@ impl<'a> Link<'a> {
)
})?;
let ftype = if !attrs.is_empty() { "rust," } else { "rust" };
if !contents.ends_with('\n') {
contents.push('\n');
}
Ok(format!(
"```{}{}\n{}```\n",
"```{}{}\n{}\n```\n",
ftype,
attrs.join(","),
contents
))
}
LinkType::Title(title) => {
*chapter_title = title.to_owned();
Ok(String::new())
}
}
}
}
@@ -406,17 +370,17 @@ impl<'a> Iterator for LinkIter<'a> {
fn find_links(contents: &str) -> LinkIter<'_> {
// lazily compute following regex
// r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([^}]+)\}\}")?;
// r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([a-zA-Z0-9_.\-:/\\\s]+)\}\}")?;
lazy_static! {
static ref RE: Regex = Regex::new(
r"(?x) # insignificant whitespace mode
\\\{\{\#.*\}\} # match escaped link
| # or
\{\{\s* # link opening parens and whitespace
\#([a-zA-Z0-9_]+) # link type
\s+ # separating whitespace
([^}]+) # link target path and space separated properties
\}\} # link closing parens"
r"(?x) # insignificant whitespace mode
\\\{\{\#.*\}\} # match escaped link
| # or
\{\{\s* # link opening parens and whitespace
\#([a-zA-Z0-9_]+) # link type
\s+ # separating whitespace
([a-zA-Z0-9\s_.\-:/\\\+]+) # link target path and space separated properties
\s*\}\} # whitespace and link closing parens"
)
.unwrap();
}
@@ -439,21 +403,7 @@ mod tests {
```hbs
{{#include file.rs}} << an escaped link!
```";
let mut chapter_title = "test_replace_all_escaped".to_owned();
assert_eq!(replace_all(start, "", "", 0, &mut chapter_title), end);
}
#[test]
fn test_set_chapter_title() {
let start = r"{{#title My Title}}
# My Chapter
";
let end = r"
# My Chapter
";
let mut chapter_title = "test_set_chapter_title".to_owned();
assert_eq!(replace_all(start, "", "", 0, &mut chapter_title), end);
assert_eq!(chapter_title, "My Title");
assert_eq!(replace_all(start, "", "", 0), end);
}
#[test]

View File

@@ -12,8 +12,6 @@ use crate::book::Book;
use crate::config::Config;
use crate::errors::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::path::PathBuf;
/// Extra information for a `Preprocessor` to give them more context when
@@ -29,8 +27,6 @@ pub struct PreprocessorContext {
/// The calling `mdbook` version.
pub mdbook_version: String,
#[serde(skip)]
pub(crate) chapter_titles: RefCell<HashMap<PathBuf, String>>,
#[serde(skip)]
__non_exhaustive: (),
}
@@ -42,7 +38,6 @@ impl PreprocessorContext {
config,
renderer,
mdbook_version: crate::MDBOOK_VERSION.to_string(),
chapter_titles: RefCell::new(HashMap::new()),
__non_exhaustive: (),
}
}

View File

@@ -37,18 +37,6 @@ impl HtmlHandlebars {
_ => return Ok(()),
};
if let Some(ref edit_url_template) = ctx.html_config.edit_url_template {
let full_path = "src/".to_owned()
+ ch.source_path
.clone()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let edit_url = edit_url_template.replace("{path}", &full_path);
ctx.data
.insert("git_repository_edit_url".to_owned(), json!(edit_url));
}
let content = ch.content.clone();
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
@@ -57,12 +45,6 @@ impl HtmlHandlebars {
ctx.html_config.curly_quotes,
Some(&path),
);
if !ctx.is_index {
// Add page break between chapters
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
// Add both two CSS properties because of the compatibility issue
print_content.push_str(r#"<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div>"#);
}
print_content.push_str(&fixed_content);
// Update the context with data for this file
@@ -82,12 +64,9 @@ impl HtmlHandlebars {
.and_then(serde_json::Value::as_str)
.unwrap_or("");
let title = if let Some(title) = ctx.chapter_titles.get(path) {
title.clone()
} else if book_title.is_empty() {
ch.name.clone()
} else {
ch.name.clone() + " - " + book_title
let title = match book_title {
"" => ch.name.clone(),
_ => ch.name.clone() + " - " + book_title,
};
ctx.data.insert("path".to_owned(), json!(path));
@@ -387,7 +366,7 @@ impl HtmlHandlebars {
// Note: all paths are relative to the build directory, so the
// leading slash in an absolute path means nothing (and would mess
// up `root.join(original)`).
let original = original.trim_start_matches('/');
let original = original.trim_start_matches("/");
let filename = root.join(original);
self.emit_redirect(handlebars, &filename, new)?;
}
@@ -473,7 +452,7 @@ impl Renderer for HtmlHandlebars {
let mut handlebars = Handlebars::new();
let theme_dir = match html_config.theme {
Some(ref theme) => ctx.root.join(theme),
Some(ref theme) => theme.to_path_buf(),
None => ctx.root.join("theme"),
};
@@ -522,7 +501,6 @@ impl Renderer for HtmlHandlebars {
is_index,
html_config: html_config.clone(),
edition: ctx.config.rust.edition,
chapter_titles: &ctx.chapter_titles,
};
self.render_item(item, ctx, &mut print_content)?;
is_index = false;
@@ -778,7 +756,7 @@ fn insert_link_into_header(
*id_count += 1;
format!(
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"##,
r##"<h{level}><a class="header" href="#{id}" id="{id}">{text}</a></h{level}>"##,
level = level,
id = id,
text = content
@@ -921,10 +899,10 @@ fn partition_source(s: &str) -> (String, String) {
if !header || after_header {
after_header = true;
after.push_str(line);
after.push('\n');
after.push_str("\n");
} else {
before.push_str(line);
before.push('\n');
before.push_str("\n");
}
}
@@ -938,7 +916,6 @@ struct RenderItemContext<'a> {
is_index: bool,
html_config: HtmlConfig,
edition: Option<RustEdition>,
chapter_titles: &'a HashMap<PathBuf, String>,
}
#[cfg(test)]
@@ -950,27 +927,27 @@ mod tests {
let inputs = vec![
(
"blah blah <h1>Foo</h1>",
r##"blah blah <h1 id="foo"><a class="header" href="#foo">Foo</a></h1>"##,
r##"blah blah <h1><a class="header" href="#foo" id="foo">Foo</a></h1>"##,
),
(
"<h1>Foo</h1>",
r##"<h1 id="foo"><a class="header" href="#foo">Foo</a></h1>"##,
r##"<h1><a class="header" href="#foo" id="foo">Foo</a></h1>"##,
),
(
"<h3>Foo^bar</h3>",
r##"<h3 id="foobar"><a class="header" href="#foobar">Foo^bar</a></h3>"##,
r##"<h3><a class="header" href="#foobar" id="foobar">Foo^bar</a></h3>"##,
),
(
"<h4></h4>",
r##"<h4 id=""><a class="header" href="#"></a></h4>"##,
r##"<h4><a class="header" href="#" id=""></a></h4>"##,
),
(
"<h4><em>Hï</em></h4>",
r##"<h4 id="hï"><a class="header" href="#hï"><em>Hï</em></a></h4>"##,
r##"<h4><a class="header" href="#hï" id="hï"><em>Hï</em></a></h4>"##,
),
(
"<h1>Foo</h1><h3>Foo</h3>",
r##"<h1 id="foo"><a class="header" href="#foo">Foo</a></h1><h3 id="foo-1"><a class="header" href="#foo-1">Foo</a></h3>"##,
r##"<h1><a class="header" href="#foo" id="foo">Foo</a></h1><h3><a class="header" href="#foo-1" id="foo-1">Foo</a></h3>"##,
),
];

View File

@@ -1,5 +1,4 @@
use std::collections::BTreeMap;
use std::io;
use std::path::Path;
use crate::utils;
@@ -103,7 +102,7 @@ impl HelperDef for RenderToc {
// Part title
if let Some(title) = item.get("part") {
out.write("<li class=\"part-title\">")?;
write_escaped(out, title)?;
out.write(title)?;
out.write("</li>")?;
continue;
}
@@ -161,7 +160,7 @@ impl HelperDef for RenderToc {
html::push_html(&mut markdown_parsed_name, parser);
// write to the handlebars template
write_escaped(out, &markdown_parsed_name)?;
out.write(&markdown_parsed_name)?;
}
if path_exists {
@@ -205,18 +204,3 @@ fn write_li_open_tag(
li.push_str("\">");
out.write(&li)
}
fn write_escaped(out: &mut dyn Output, mut title: &str) -> io::Result<()> {
let needs_escape: &[char] = &['<', '>'];
while let Some(next) = title.find(needs_escape) {
out.write(&title[..next])?;
match title.as_bytes()[next] {
b'<' => out.write("&lt;")?,
b'>' => out.write("&gt;")?,
_ => unreachable!(),
}
title = &title[next + 1..];
}
out.write(title)?;
Ok(())
}

View File

@@ -95,8 +95,6 @@ fn render_item(
let mut breadcrumbs = chapter.parent_names.clone();
let mut footnote_numbers = HashMap::new();
breadcrumbs.push(chapter.name.clone());
while let Some(event) = p.next() {
match event {
Event::Start(Tag::Heading(i)) if i <= max_section_depth => {

View File

@@ -18,10 +18,9 @@ mod html_handlebars;
mod markdown_renderer;
use shlex::Shlex;
use std::collections::HashMap;
use std::fs;
use std::io::{self, ErrorKind, Read};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process::{Command, Stdio};
use crate::book::Book;
@@ -35,9 +34,12 @@ use toml::Value;
/// provide your own renderer, there are two main renderer implementations that
/// 99% of users will ever use:
///
/// - [`HtmlHandlebars`] - the built-in HTML renderer
/// - [`CmdRenderer`] - a generic renderer which shells out to a program to do the
/// - [HtmlHandlebars] - the built-in HTML renderer
/// - [CmdRenderer] - a generic renderer which shells out to a program to do the
/// actual rendering
///
/// [HtmlHandlebars]: struct.HtmlHandlebars.html
/// [CmdRenderer]: struct.CmdRenderer.html
pub trait Renderer {
/// The `Renderer`'s name.
fn name(&self) -> &str;
@@ -65,8 +67,6 @@ pub struct RenderContext {
/// guaranteed to be empty or even exist.
pub destination: PathBuf,
#[serde(skip)]
pub(crate) chapter_titles: HashMap<PathBuf, String>,
#[serde(skip)]
__non_exhaustive: (),
}
@@ -83,7 +83,6 @@ impl RenderContext {
version: crate::MDBOOK_VERSION.to_string(),
root: root.into(),
destination: destination.into(),
chapter_titles: HashMap::new(),
__non_exhaustive: (),
}
}
@@ -134,44 +133,14 @@ impl CmdRenderer {
CmdRenderer { name, cmd }
}
fn compose_command(&self, root: &Path, destination: &Path) -> Result<Command> {
fn compose_command(&self) -> Result<Command> {
let mut words = Shlex::new(&self.cmd);
let exe = match words.next() {
Some(e) => PathBuf::from(e),
let executable = match words.next() {
Some(e) => e,
None => bail!("Command string was empty"),
};
let exe = if exe.components().count() == 1 {
// Search PATH for the executable.
exe
} else {
// Relative paths are preferred to be relative to the book root.
let abs_exe = root.join(&exe);
if abs_exe.exists() {
abs_exe
} else {
// Historically paths were relative to the destination, but
// this is not the preferred way.
let legacy_path = destination.join(&exe);
if legacy_path.exists() {
warn!(
"Renderer command `{}` uses a path relative to the \
renderer output directory `{}`. This was previously \
accepted, but has been deprecated. Relative executable \
paths should be relative to the book root.",
exe.display(),
destination.display()
);
legacy_path
} else {
// Let this bubble through to later be handled by
// handle_render_command_error.
abs_exe.to_path_buf()
}
}
};
let mut cmd = Command::new(exe);
let mut cmd = Command::new(executable);
for arg in words {
cmd.arg(arg);
@@ -226,7 +195,7 @@ impl Renderer for CmdRenderer {
let _ = fs::create_dir_all(&ctx.destination);
let mut child = match self
.compose_command(&ctx.root, &ctx.destination)?
.compose_command()?
.stdin(Stdio::piped())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())

View File

@@ -93,7 +93,7 @@ a > .hljs {
.menu-title {
display: inline-block;
font-weight: 200;
font-size: 2.4rem;
font-size: 2rem;
line-height: var(--menu-bar-height);
text-align: center;
margin: 0;

View File

@@ -45,23 +45,20 @@ h4, h5 { margin-top: 2em; }
margin-top: 1em;
}
h1:target::before,
h2:target::before,
h3:target::before,
h4:target::before,
h5:target::before,
h6:target::before {
h1 a.header:target::before,
h2 a.header:target::before,
h3 a.header:target::before,
h4 a.header:target::before {
display: inline-block;
content: "»";
margin-left: -30px;
width: 30px;
}
/* This is broken on Safari as of version 14, but is fixed
in Safari Technology Preview 117 which I think will be Safari 14.2.
https://bugs.webkit.org/show_bug.cgi?id=218076
*/
:target {
h1 a.header:target,
h2 a.header:target,
h3 a.header:target,
h4 a.header:target {
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}

View File

@@ -92,22 +92,22 @@
.light {
--bg: hsl(0, 0%, 100%);
--fg: hsl(0, 0%, 0%);
--fg: #333333;
--sidebar-bg: #fafafa;
--sidebar-fg: hsl(0, 0%, 0%);
--sidebar-fg: #364149;
--sidebar-non-existant: #aaaaaa;
--sidebar-active: #1f1fff;
--sidebar-active: #008cff;
--sidebar-spacer: #f4f4f4;
--scrollbar: #8F8F8F;
--scrollbar: #cccccc;
--icons: #747474;
--icons-hover: #000000;
--icons: #cccccc;
--icons-hover: #333333;
--links: #20609f;
--links: #4183c4;
--inline-code-color: #301900;
--inline-code-color: #6e6b5e;
--theme-popup-bg: #fafafa;
--theme-popup-border: #cccccc;

View File

@@ -1,18 +1,14 @@
/*
* An increased contrast highlighting scheme loosely based on the
* "Base16 Atelier Dune Light" theme by Bram de Haan
* (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
* Original Base16 color scheme by Chris Kempson
* (https://github.com/chriskempson/base16)
*/
/* Base16 Atelier Dune Light - Theme */
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
/* Comment */
/* Atelier-Dune Comment */
.hljs-comment,
.hljs-quote {
color: #575757;
color: #AAA;
}
/* Red */
/* Atelier-Dune Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
@@ -23,10 +19,10 @@
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #d70025;
color: #d73737;
}
/* Orange */
/* Atelier-Dune Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
@@ -34,33 +30,33 @@
.hljs-literal,
.hljs-type,
.hljs-params {
color: #b21e00;
color: #b65611;
}
/* Green */
/* Atelier-Dune Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #008200;
color: #60ac39;
}
/* Blue */
/* Atelier-Dune Blue */
.hljs-title,
.hljs-section {
color: #0030f2;
color: #6684e1;
}
/* Purple */
/* Atelier-Dune Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #9d00ec;
color: #b854d4;
}
.hljs {
display: block;
overflow-x: auto;
background: #f6f7f6;
color: #000;
background: #f1f1f1;
color: #6e6b5e;
padding: 0.5em;
}

View File

@@ -148,19 +148,13 @@
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
</a>
{{/if}}
{{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{/if}}
</div>
</div>
{{#if search_enabled}}
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>

View File

@@ -296,7 +296,7 @@ window.search = window.search || {};
}
if (url.params.hasOwnProperty(URL_MARK_PARAM)) {
var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' ');
var words = url.params[URL_MARK_PARAM].split(' ');
marker.mark(words, {
exclude: mark_exclude
});
@@ -427,7 +427,6 @@ window.search = window.search || {};
delete url.params[URL_MARK_PARAM];
url.hash = "";
} else {
delete url.params[URL_MARK_PARAM];
delete url.params[URL_SEARCH_PARAM];
}
// A new search will also add a new history item, so the user can go back

View File

@@ -110,10 +110,7 @@ pub fn copy_files_except_ext(
for entry in fs::read_dir(from)? {
let entry = entry?;
let metadata = entry
.path()
.metadata()
.with_context(|| format!("Failed to read {:?}", entry.path()))?;
let metadata = entry.path().metadata()?;
// If the entry is a dir and the recursive option is enabled, call itself
if metadata.is_dir() && recursive {

View File

@@ -70,7 +70,7 @@ pub fn take_rustdoc_include_lines<R: RangeBounds<usize>>(s: &str, range: R) -> S
output.push_str("# ");
}
output.push_str(line);
output.push('\n');
output.push_str("\n");
}
output.pop();
output
@@ -95,7 +95,7 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
None => {
if !ANCHOR_START.is_match(l) {
output.push_str(l);
output.push('\n');
output.push_str("\n");
}
}
}
@@ -106,7 +106,7 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
} else if !ANCHOR_END.is_match(l) {
output.push_str("# ");
output.push_str(l);
output.push('\n');
output.push_str("\n");
}
}

View File

@@ -53,7 +53,7 @@ impl TomlExt for Value {
}
fn split(key: &str) -> Option<(&str, &str)> {
let ix = key.find('.')?;
let ix = key.find(".")?;
let (head, tail) = key.split_at(ix);
// splitting will leave the "."

View File

@@ -2,7 +2,7 @@
use mdbook::config::Config;
use mdbook::MDBook;
use std::fs;
#[cfg(not(windows))]
use std::path::Path;
use tempfile::{Builder as TempFileBuilder, TempDir};
@@ -71,45 +71,6 @@ fn backends_receive_render_context_via_stdin() {
assert!(got.is_ok());
}
#[test]
fn relative_command_path() {
// Checks behavior of relative paths for the `command` setting.
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
let renderers = temp.path().join("renderers");
fs::create_dir(&renderers).unwrap();
rust_exe(
&renderers,
"myrenderer",
r#"fn main() {
std::fs::write("output", "test").unwrap();
}"#,
);
let do_test = |cmd_path| {
let mut config = Config::default();
config
.set("output.html", toml::value::Table::new())
.unwrap();
config.set("output.myrenderer.command", cmd_path).unwrap();
let md = MDBook::init(&temp.path())
.with_config(config)
.build()
.unwrap();
let output = temp.path().join("book/myrenderer/output");
assert!(!output.exists());
md.build().unwrap();
assert!(output.exists());
fs::remove_file(output).unwrap();
};
// Legacy paths work, relative to the output directory.
if cfg!(windows) {
do_test("../../renderers/myrenderer.exe");
} else {
do_test("../../renderers/myrenderer");
}
// Modern path, relative to the book directory.
do_test("renderers/myrenderer");
}
fn dummy_book_with_backend(
name: &str,
command: &str,
@@ -151,14 +112,3 @@ fn success_cmd() -> &'static str {
"true"
}
}
fn rust_exe(temp: &Path, name: &str, src: &str) {
let rs = temp.join(name).with_extension("rs");
fs::write(&rs, src).unwrap();
let status = std::process::Command::new("rustc")
.arg(rs)
.current_dir(temp)
.status()
.expect("rustc should run");
assert!(status.success());
}

View File

@@ -91,12 +91,6 @@ fn run_mdbook_init_with_custom_book_and_src_locations() {
file
);
}
let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap();
assert_eq!(
contents,
"[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"in\"\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nuse-default-preprocessors = true\n"
);
}
#[test]
@@ -108,37 +102,3 @@ fn book_toml_isnt_required() {
md.build().unwrap();
}
#[test]
fn copy_theme() {
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
MDBook::init(temp.path()).copy_theme(true).build().unwrap();
let expected = vec![
"book.js",
"css/chrome.css",
"css/general.css",
"css/print.css",
"css/variables.css",
"favicon.png",
"favicon.svg",
"highlight.css",
"highlight.js",
"index.hbs",
];
let theme_dir = temp.path().join("theme");
let mut actual: Vec<_> = walkdir::WalkDir::new(&theme_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| !e.file_type().is_dir())
.map(|e| {
e.path()
.strip_prefix(&theme_dir)
.unwrap()
.to_str()
.unwrap()
.replace('\\', "/")
})
.collect();
actual.sort();
assert_eq!(actual, expected);
}

View File

@@ -104,12 +104,12 @@ fn check_correct_cross_links_in_nested_dir() {
assert_contains_strings(
first.join("index.html"),
&[r##"<h2 id="some-section"><a class="header" href="#some-section">"##],
&[r##"href="#some-section" id="some-section""##],
);
assert_contains_strings(
first.join("nested.html"),
&[r##"<h2 id="some-section"><a class="header" href="#some-section">"##],
&[r##"href="#some-section" id="some-section""##],
);
}
@@ -373,7 +373,7 @@ fn able_to_include_files_in_chapters() {
let includes = temp.path().join("book/first/includes.html");
let summary_strings = &[
r##"<h1 id="summary"><a class="header" href="#summary">Summary</a></h1>"##,
r##"<h1><a class="header" href="#summary" id="summary">Summary</a></h1>"##,
">First Chapter</a>",
];
assert_contains_strings(&includes, summary_strings);
@@ -595,10 +595,7 @@ mod search {
docs[&summary]["body"],
"Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode Second Chapter Nested Chapter Conclusion"
);
assert_eq!(
docs[&summary]["breadcrumbs"],
"First Chapter » Includes » Summary"
);
assert_eq!(docs[&summary]["breadcrumbs"], "First Chapter » Summary");
assert_eq!(docs[&conclusion]["body"], "I put &lt;HTML&gt; in here!");
}

View File

@@ -27,234 +27,234 @@
"docInfo": {
"0": {
"body": 9,
"breadcrumbs": 4,
"breadcrumbs": 2,
"title": 2
},
"1": {
"body": 3,
"breadcrumbs": 2,
"breadcrumbs": 1,
"title": 1
},
"10": {
"body": 16,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
},
"11": {
"body": 3,
"breadcrumbs": 5,
"breadcrumbs": 4,
"title": 2
},
"12": {
"body": 4,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
},
"13": {
"body": 12,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
},
"14": {
"body": 2,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
},
"15": {
"body": 3,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
},
"16": {
"body": 29,
"breadcrumbs": 6,
"breadcrumbs": 5,
"title": 3
},
"17": {
"body": 20,
"breadcrumbs": 4,
"breadcrumbs": 2,
"title": 2
},
"18": {
"body": 18,
"breadcrumbs": 9,
"breadcrumbs": 7,
"title": 5
},
"19": {
"body": 0,
"breadcrumbs": 5,
"breadcrumbs": 3,
"title": 1
},
"2": {
"body": 2,
"breadcrumbs": 4,
"breadcrumbs": 2,
"title": 2
},
"20": {
"body": 3,
"breadcrumbs": 2,
"breadcrumbs": 1,
"title": 1
},
"3": {
"body": 0,
"breadcrumbs": 3,
"breadcrumbs": 1,
"title": 1
},
"4": {
"body": 4,
"breadcrumbs": 6,
"breadcrumbs": 4,
"title": 2
},
"5": {
"body": 1,
"breadcrumbs": 5,
"breadcrumbs": 3,
"title": 1
},
"6": {
"body": 21,
"breadcrumbs": 11,
"breadcrumbs": 9,
"title": 7
},
"7": {
"body": 6,
"breadcrumbs": 10,
"breadcrumbs": 8,
"title": 6
},
"8": {
"body": 6,
"breadcrumbs": 8,
"breadcrumbs": 6,
"title": 4
},
"9": {
"body": 0,
"breadcrumbs": 4,
"breadcrumbs": 3,
"title": 1
}
},
"docs": {
"0": {
"body": "This file is just here to cause the index preprocessor to run. Does a pretty good job, too.",
"breadcrumbs": "Dummy Book » Dummy Book",
"breadcrumbs": "Dummy Book",
"id": "0",
"title": "Dummy Book"
},
"1": {
"body": "Here's some interesting text...",
"breadcrumbs": "Introduction » Introduction",
"breadcrumbs": "Introduction",
"id": "1",
"title": "Introduction"
},
"10": {
"body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode Second Chapter Nested Chapter Conclusion",
"breadcrumbs": "First Chapter » Includes » Summary",
"breadcrumbs": "First Chapter » Summary",
"id": "10",
"title": "Summary"
},
"11": {
"body": "Tests for some markdown output.",
"breadcrumbs": "First Chapter » Markdown » Markdown tests",
"breadcrumbs": "First Chapter » Markdown tests",
"id": "11",
"title": "Markdown tests"
},
"12": {
"body": "foo bar baz bim",
"breadcrumbs": "First Chapter » Markdown » Tables",
"breadcrumbs": "First Chapter » Tables",
"id": "12",
"title": "Tables"
},
"13": {
"body": "Footnote example [1] , or with a word [2] . This is a footnote. A longer footnote. With multiple lines. Third line.",
"breadcrumbs": "First Chapter » Markdown » Footnotes",
"breadcrumbs": "First Chapter » Footnotes",
"id": "13",
"title": "Footnotes"
},
"14": {
"body": "strikethrough example",
"breadcrumbs": "First Chapter » Markdown » Strikethrough",
"breadcrumbs": "First Chapter » Strikethrough",
"id": "14",
"title": "Strikethrough"
},
"15": {
"body": "Apples Broccoli Carrots",
"breadcrumbs": "First Chapter » Markdown » Tasklisks",
"breadcrumbs": "First Chapter » Tasklisks",
"id": "15",
"title": "Tasklisks"
},
"16": {
"body": "Please be careful editing, this contains carefully crafted characters. Two byte character: spatiëring Combining character: spatiëring Three byte character: 书こんにちは Four byte character: 𐌀‮𐌁‮𐌂‮𐌃‮𐌄‮𐌅‮𐌆‮𐌇‮𐌈‬ Right-to-left: مرحبا Emoticons: 🔊 😍 💜 1⃣ right-to-left mark: hello באמת! Zalgo: ǫ̛̖̱̗̝͈̋͒͋̏ͥͫ̒̆ͩ̏͌̾͊͐ͪ̾̚",
"breadcrumbs": "First Chapter » Unicode » Unicode stress tests",
"breadcrumbs": "First Chapter » Unicode stress tests",
"id": "16",
"title": "Unicode stress tests"
},
"17": {
"body": "This makes sure you can insert runnable Rust files. fn main() { println!(\"Hello World!\");\n#\n# // You can even hide lines! :D\n# println!(\"I am hidden! Expand the code snippet to see me\");\n}",
"breadcrumbs": "Second Chapter » Second Chapter",
"breadcrumbs": "Second Chapter",
"id": "17",
"title": "Second Chapter"
},
"18": {
"body": "When we link to the first section , it should work on both the print page and the non-print page. A fragment link should work. Link outside . Some image HTML Link",
"breadcrumbs": "Second Chapter » Nested Chapter » Testing relative links for the print page",
"breadcrumbs": "Second Chapter » Testing relative links for the print page",
"id": "18",
"title": "Testing relative links for the print page"
},
"19": {
"body": "",
"breadcrumbs": "Second Chapter » Nested Chapter » Some section",
"breadcrumbs": "Second Chapter » Some section",
"id": "19",
"title": "Some section"
},
"2": {
"body": "more text.",
"breadcrumbs": "First Chapter » First Chapter",
"breadcrumbs": "First Chapter",
"id": "2",
"title": "First Chapter"
},
"20": {
"body": "I put &lt;HTML&gt; in here!",
"breadcrumbs": "Conclusion » Conclusion",
"breadcrumbs": "Conclusion",
"id": "20",
"title": "Conclusion"
},
"3": {
"body": "",
"breadcrumbs": "First Chapter » Some Section",
"breadcrumbs": "Some Section",
"id": "3",
"title": "Some Section"
},
"4": {
"body": "This file has some testable code. assert!(true);",
"breadcrumbs": "First Chapter » Nested Chapter » Nested Chapter",
"breadcrumbs": "First Chapter » Nested Chapter",
"id": "4",
"title": "Nested Chapter"
},
"5": {
"body": "assert!(true);",
"breadcrumbs": "First Chapter » Nested Chapter » Some Section",
"breadcrumbs": "First Chapter » Some Section",
"id": "5",
"title": "Some Section"
},
"6": {
"body": "// The next line will cause a `rendered_output` test to fail if the anchor feature is broken in\n// such a way that the content between anchors isn't included.\n// unique-string-for-anchor-test\nassert!(true);",
"breadcrumbs": "First Chapter » Nested Chapter » Anchors include the part of a file between special comments",
"breadcrumbs": "First Chapter » Anchors include the part of a file between special comments",
"id": "6",
"title": "Anchors include the part of a file between special comments"
},
"7": {
"body": "# fn some_function() {\n# assert!(true);\n# }\n# fn main() { some_function();\n}",
"breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include adds the rest of the file as hidden",
"breadcrumbs": "First Chapter » Rustdoc include adds the rest of the file as hidden",
"id": "7",
"title": "Rustdoc include adds the rest of the file as hidden"
},
"8": {
"body": "# fn some_other_function() {\n# assert!(true);\n# }\n# fn main() { some_other_function();\n}",
"breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include works with anchors too",
"breadcrumbs": "First Chapter » Rustdoc include works with anchors too",
"id": "8",
"title": "Rustdoc include works with anchors too"
},
"9": {
"body": "",
"breadcrumbs": "First Chapter » Includes » Includes",
"breadcrumbs": "First Chapter » Includes",
"id": "9",
"title": "Includes"
}
@@ -3048,7 +3048,7 @@
"df": 2,
"docs": {
"0": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
},
"10": {
"tf": 1.0
@@ -3222,7 +3222,7 @@
"df": 0,
"docs": {},
"r": {
"df": 18,
"df": 17,
"docs": {
"10": {
"tf": 2.23606797749979
@@ -3246,34 +3246,31 @@
"tf": 1.0
},
"17": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
},
"18": {
"tf": 1.4142135623730951
},
"19": {
"tf": 1.4142135623730951
},
"2": {
"tf": 1.7320508075688772
},
"3": {
"tf": 1.0
},
"19": {
"tf": 1.0
},
"2": {
"tf": 1.4142135623730951
},
"4": {
"tf": 2.0
"tf": 1.7320508075688772
},
"5": {
"tf": 1.4142135623730951
"tf": 1.0
},
"6": {
"tf": 1.4142135623730951
"tf": 1.0
},
"7": {
"tf": 1.4142135623730951
"tf": 1.0
},
"8": {
"tf": 1.4142135623730951
"tf": 1.0
},
"9": {
"tf": 1.0
@@ -3382,7 +3379,7 @@
"tf": 1.0
},
"20": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
}
}
}
@@ -3470,7 +3467,7 @@
"df": 2,
"docs": {
"0": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
},
"10": {
"tf": 1.0
@@ -3675,7 +3672,7 @@
"df": 0,
"docs": {},
"t": {
"df": 16,
"df": 15,
"docs": {
"10": {
"tf": 1.4142135623730951
@@ -3702,10 +3699,7 @@
"tf": 1.0
},
"2": {
"tf": 1.7320508075688772
},
"3": {
"tf": 1.0
"tf": 1.4142135623730951
},
"4": {
"tf": 1.0
@@ -3970,7 +3964,7 @@
"df": 5,
"docs": {
"10": {
"tf": 1.4142135623730951
"tf": 1.0
},
"6": {
"tf": 1.7320508075688772
@@ -3982,7 +3976,7 @@
"tf": 1.4142135623730951
},
"9": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
}
}
},
@@ -4071,7 +4065,7 @@
"df": 2,
"docs": {
"1": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
},
"10": {
"tf": 1.0
@@ -4288,25 +4282,13 @@
"df": 0,
"docs": {},
"n": {
"df": 6,
"df": 2,
"docs": {
"10": {
"tf": 1.0
},
"11": {
"tf": 2.0
},
"12": {
"tf": 1.0
},
"13": {
"tf": 1.0
},
"14": {
"tf": 1.0
},
"15": {
"tf": 1.0
"tf": 1.7320508075688772
}
}
}
@@ -4379,31 +4361,13 @@
"df": 0,
"docs": {},
"t": {
"df": 8,
"df": 2,
"docs": {
"10": {
"tf": 1.4142135623730951
},
"18": {
"tf": 1.0
},
"19": {
"tf": 1.0
},
"4": {
"tf": 1.7320508075688772
},
"5": {
"tf": 1.0
},
"6": {
"tf": 1.0
},
"7": {
"tf": 1.0
},
"8": {
"tf": 1.0
"tf": 1.4142135623730951
}
}
}
@@ -4879,7 +4843,7 @@
"tf": 1.0
},
"17": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
},
"18": {
"tf": 1.0
@@ -5421,7 +5385,7 @@
"tf": 1.0
},
"16": {
"tf": 1.7320508075688772
"tf": 1.4142135623730951
}
}
},