mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-28 11:24:57 -05:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7181993b43 | ||
|
|
bf9f58e11b | ||
|
|
3ba71c570c | ||
|
|
674e58e747 | ||
|
|
348c5d07c5 | ||
|
|
1790b04e03 | ||
|
|
50ee15472b | ||
|
|
ffb90bb9e2 | ||
|
|
186e649530 | ||
|
|
adc1f4ade7 | ||
|
|
b777a318f7 | ||
|
|
30e3b83167 | ||
|
|
f082187844 | ||
|
|
6119972fa7 | ||
|
|
a910435fd9 | ||
|
|
53b902b479 | ||
|
|
2e9d8671a0 | ||
|
|
50cdfc9623 | ||
|
|
d47f4dce7f | ||
|
|
bda23f0183 | ||
|
|
1fbad982d8 |
20
.travis.yml
20
.travis.yml
@@ -28,10 +28,10 @@ matrix:
|
||||
- env: TARGET=x86_64-unknown-linux-musl
|
||||
|
||||
# Mac
|
||||
# - env: TARGET=i686-apple-darwin
|
||||
# os: osx
|
||||
# - env: TARGET=x86_64-apple-darwin
|
||||
# os: osx
|
||||
- env: TARGET=i686-apple-darwin
|
||||
os: osx
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
|
||||
# BSD
|
||||
- env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1
|
||||
@@ -41,14 +41,14 @@ matrix:
|
||||
# Other channels
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
rust: beta
|
||||
# - env: TARGET=x86_64-apple-darwin
|
||||
# os: osx
|
||||
# rust: beta
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: beta
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
rust: nightly
|
||||
# - env: TARGET=x86_64-apple-darwin
|
||||
# os: osx
|
||||
# rust: nightly
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: nightly
|
||||
|
||||
before_install:
|
||||
- set -e
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mdbook"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
authors = ["Mathieu David <mathieudavid@mathieudavid.org>", "Michael-F-Bryan <michaelfbryan@gmail.com>"]
|
||||
description = "create books from markdown files (like Gitbook)"
|
||||
documentation = "http://rust-lang-nursery.github.io/mdBook/index.html"
|
||||
@@ -14,6 +14,11 @@ exclude = [
|
||||
"src/theme/stylus/**",
|
||||
]
|
||||
|
||||
[package.metadata.release]
|
||||
sign-commit = true
|
||||
push-remote = "upstream"
|
||||
tag-prefix = "v"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.24"
|
||||
chrono = "0.4"
|
||||
@@ -65,6 +70,3 @@ serve = ["iron", "staticfile", "ws"]
|
||||
doc = false
|
||||
name = "mdbook"
|
||||
path = "src/bin/mdbook.rs"
|
||||
|
||||
[workspace]
|
||||
members = ["book-example/src/for_developers/mdbook-wordcount"]
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
- [Syntax highlighting](format/theme/syntax-highlighting.md)
|
||||
- [Editor](format/theme/editor.md)
|
||||
- [MathJax Support](format/mathjax.md)
|
||||
- [Rust code specific features](format/rust.md)
|
||||
- [mdBook specific features](format/mdbook.md)
|
||||
- [For Developers](for_developers/index.md)
|
||||
- [Preprocessors](for_developers/preprocessors.md)
|
||||
- [Alternate Backends](for_developers/backends.md)
|
||||
|
||||
@@ -21,7 +21,7 @@ mdbook serve path/to/book
|
||||
For example: suppose you had 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 server path/to/book -p 8000 -i 127.0.0.1 -a 192.168.1.100
|
||||
mdbook serve path/to/book -p 8000 -i 127.0.0.1 -a 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.
|
||||
|
||||
@@ -116,7 +116,7 @@ tables. If none are provided it'll fall back to using the default HTML
|
||||
renderer.
|
||||
|
||||
Notably, this means if you want to add your own custom backend you'll also
|
||||
need to make sure to add the HTML backend, even if its tabke just stays empty.
|
||||
need to make sure to add the HTML backend, even if its table just stays empty.
|
||||
|
||||
Now you just need to build your book like normal, and everything should *Just
|
||||
Work*.
|
||||
@@ -149,7 +149,7 @@ The reason we didn't need to specify the full name/path of our `wordcount`
|
||||
backend is because `mdbook` will try to *infer* the program's name via
|
||||
convention. The executable for the `foo` backend is typically called
|
||||
`mdbook-foo`, with an associated `[output.foo]` entry in the `book.toml`. To
|
||||
explicitly tell `mdbook` what command to invoke (it may require command line
|
||||
explicitly tell `mdbook` what command to invoke (it may require command-line
|
||||
arguments or be an interpreted script), you can use the `command` field.
|
||||
|
||||
```diff
|
||||
@@ -349,4 +349,4 @@ the source code or ask questions.
|
||||
[`Book`]: http://rust-lang-nursery.github.io/mdBook/mdbook/book/struct.Book.html
|
||||
[`Book::iter()`]: http://rust-lang-nursery.github.io/mdBook/mdbook/book/struct.Book.html#method.iter
|
||||
[`Config`]: http://rust-lang-nursery.github.io/mdBook/mdbook/config/struct.Config.html
|
||||
[issue tracker]: https://github.com/rust-lang-nursery/mdBook/issues
|
||||
[issue tracker]: https://github.com/rust-lang-nursery/mdBook/issues
|
||||
|
||||
@@ -21,7 +21,7 @@ The process of rendering a book project goes through several steps.
|
||||
|
||||
1. Load the book
|
||||
- Parse the `book.toml`, falling back to the default `Config` if it doesn't
|
||||
exist.
|
||||
exist
|
||||
- Load the book chapters into memory
|
||||
- Discover which preprocessors/backends should be used
|
||||
2. Run the preprocessors
|
||||
@@ -43,4 +43,4 @@ explanation on the configuration system.
|
||||
|
||||
[`MDBook`]: http://rust-lang-nursery.github.io/mdBook/mdbook/book/struct.MDBook.html
|
||||
[API Docs]: http://rust-lang-nursery.github.io/mdBook/mdbook/
|
||||
[config]: file:///home/michael/Documents/forks/mdBook/target/doc/mdbook/config/index.html
|
||||
[config]: file:///home/michael/Documents/forks/mdBook/target/doc/mdbook/config/index.html
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
name = "mdbook-wordcount"
|
||||
version = "0.1.0"
|
||||
authors = ["Michael Bryan <michaelfbryan@gmail.com>"]
|
||||
workspace = "../../../.."
|
||||
|
||||
[dependencies]
|
||||
mdbook = { path = "../../../.." }
|
||||
mdbook = { path = "../../../..", version = "*" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
64
book-example/src/format/mdbook.md
Normal file
64
book-example/src/format/mdbook.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# mdBook specific markdown
|
||||
|
||||
## Hiding code lines
|
||||
|
||||
There is a feature in mdBook that let's you hide code lines by prepending them with a `#`.
|
||||
|
||||
```bash
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 6;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
Will render as
|
||||
|
||||
```rust
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 7;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
## Including files
|
||||
|
||||
With the following syntax, you can include files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#include file.rs}}
|
||||
```
|
||||
|
||||
The path to the file has to be relative from the current source file.
|
||||
|
||||
Usually, this command is used for including code snippets and examples. In this case, oftens one would include a specific part of the file e.g. which only contains the relevant lines for the example. We support four different modes of partial includes:
|
||||
|
||||
```hbs
|
||||
\{{#include file.rs:2}}
|
||||
\{{#include file.rs::10}}
|
||||
\{{#include file.rs:2:}}
|
||||
\{{#include file.rs:2:10}}
|
||||
```
|
||||
|
||||
The first command only includes the second line from file `file.rs`. The second command includes all lines up to line 10, i.e. the lines from 11 till the end of the file are omitted. The third command includes all lines from line 2, i.e. the first line is omitted. The last command includes the excerpt of `file.rs` consisting of lines 2 to 10.
|
||||
|
||||
## Inserting runnable Rust files
|
||||
|
||||
With the following syntax, you can insert runnable Rust files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#playpen file.rs}}
|
||||
```
|
||||
|
||||
The path to the Rust file has to be relative from the current source file.
|
||||
|
||||
When play is clicked, the code snippet will be send to the [Rust Playpen] to be compiled and run. The result is send back and displayed directly underneath the code.
|
||||
|
||||
Here is what a rendered code snippet looks like:
|
||||
|
||||
{{#playpen example.rs}}
|
||||
|
||||
[Rust Playpen]: https://play.rust-lang.org/
|
||||
@@ -1,44 +0,0 @@
|
||||
# Rust code specific features
|
||||
|
||||
## Hiding code lines
|
||||
|
||||
There is a feature in mdBook that let's you hide code lines by prepending them with a `#`.
|
||||
|
||||
```bash
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 6;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
Will render as
|
||||
|
||||
```rust
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 7;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
|
||||
## Inserting runnable Rust files
|
||||
|
||||
With the following syntax, you can insert runnable Rust files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#playpen file.rs}}
|
||||
```
|
||||
|
||||
The path to the Rust file has to be relative from the current source file.
|
||||
|
||||
When play is clicked, the code snippet will be send to the [Rust Playpen] to be compiled and run. The result is send back and displayed directly underneath the code.
|
||||
|
||||
Here is what a rendered code snippet looks like:
|
||||
|
||||
{{#playpen example.rs}}
|
||||
|
||||
[Rust Playpen]: https://play.rust-lang.org/
|
||||
@@ -101,6 +101,12 @@ impl Book {
|
||||
{
|
||||
for_each_mut(&mut func, &mut self.sections);
|
||||
}
|
||||
|
||||
/// Append a `BookItem` to the `Book`.
|
||||
pub fn push_item<I: Into<BookItem>>(&mut self, item: I) -> &mut Self {
|
||||
self.sections.push(item.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_mut<'a, F, I>(func: &mut F, items: I)
|
||||
@@ -126,6 +132,12 @@ pub enum BookItem {
|
||||
Separator,
|
||||
}
|
||||
|
||||
impl From<Chapter> for BookItem {
|
||||
fn from(other: Chapter) -> BookItem {
|
||||
BookItem::Chapter(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// The representation of a "chapter", usually mapping to a single file on
|
||||
/// disk however it may contain multiple sub-chapters.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
@@ -10,7 +10,7 @@ use book::{Book, BookItem};
|
||||
|
||||
const ESCAPE_CHAR: char = '\\';
|
||||
|
||||
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
|
||||
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
|
||||
/// helpers in a chapter.
|
||||
pub struct LinkPreprocessor;
|
||||
|
||||
@@ -87,8 +87,14 @@ enum LinkType<'a> {
|
||||
fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||
let mut parts = path.split(':');
|
||||
let path = parts.next().unwrap().into();
|
||||
let start = parts.next().and_then(|s| s.parse::<usize>().ok());
|
||||
let end = parts.next().and_then(|s| s.parse::<usize>().ok());
|
||||
// subtract 1 since line numbers usually begin with 1
|
||||
let start = parts
|
||||
.next()
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.map(|val| val.checked_sub(1).unwrap_or(0));
|
||||
let end = parts.next();
|
||||
let has_end = end.is_some();
|
||||
let end = end.and_then(|s| s.parse::<usize>().ok());
|
||||
match start {
|
||||
Some(start) => match end {
|
||||
Some(end) => LinkType::IncludeRange(
|
||||
@@ -98,7 +104,17 @@ fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||
end: end,
|
||||
},
|
||||
),
|
||||
None => LinkType::IncludeRangeFrom(path, RangeFrom { start: start }),
|
||||
None => if has_end {
|
||||
LinkType::IncludeRangeFrom(path, RangeFrom { start: start })
|
||||
} else {
|
||||
LinkType::IncludeRange(
|
||||
path,
|
||||
Range {
|
||||
start: start,
|
||||
end: start + 1,
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
None => match end {
|
||||
Some(end) => LinkType::IncludeRangeTo(path, RangeTo { end: end }),
|
||||
@@ -276,13 +292,31 @@ mod tests {
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 48,
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 10..20),
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 9..20),
|
||||
link_text: "{{#include file.rs:10:20}}",
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_links_with_line_number() {
|
||||
let s = "Some random text with {{#include file.rs:10}}...";
|
||||
let res = find_links(s).collect::<Vec<_>>();
|
||||
println!("\nOUTPUT: {:?}\n", res);
|
||||
assert_eq!(
|
||||
res,
|
||||
vec![
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 45,
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 9..10),
|
||||
link_text: "{{#include file.rs:10}}",
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_links_with_from_range() {
|
||||
let s = "Some random text with {{#include file.rs:10:}}...";
|
||||
@@ -294,7 +328,7 @@ mod tests {
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 46,
|
||||
link: LinkType::IncludeRangeFrom(PathBuf::from("file.rs"), 10..),
|
||||
link: LinkType::IncludeRangeFrom(PathBuf::from("file.rs"), 9..),
|
||||
link_text: "{{#include file.rs:10:}}",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -238,23 +238,28 @@ impl HtmlHandlebars {
|
||||
|
||||
/// Copy across any additional CSS and JavaScript files which the book
|
||||
/// has been configured to use.
|
||||
fn copy_additional_css_and_js(&self, html: &HtmlConfig, destination: &Path) -> Result<()> {
|
||||
fn copy_additional_css_and_js(&self, html: &HtmlConfig, root: &Path, destination: &Path) -> Result<()> {
|
||||
let custom_files = html.additional_css.iter().chain(html.additional_js.iter());
|
||||
|
||||
debug!("Copying additional CSS and JS");
|
||||
|
||||
for custom_file in custom_files {
|
||||
let input_location = root.join(custom_file);
|
||||
let output_location = destination.join(custom_file);
|
||||
if let Some(parent) = output_location.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
.chain_err(|| format!("Unable to create {}", parent.display()))?;
|
||||
}
|
||||
debug!(
|
||||
"Copying {} -> {}",
|
||||
custom_file.display(),
|
||||
input_location.display(),
|
||||
output_location.display()
|
||||
);
|
||||
|
||||
fs::copy(custom_file, &output_location).chain_err(|| {
|
||||
fs::copy(&input_location, &output_location).chain_err(|| {
|
||||
format!(
|
||||
"Unable to copy {} to {}",
|
||||
custom_file.display(),
|
||||
input_location.display(),
|
||||
output_location.display()
|
||||
)
|
||||
})?;
|
||||
@@ -334,7 +339,7 @@ impl Renderer for HtmlHandlebars {
|
||||
debug!("Copy static files");
|
||||
self.copy_static_files(&destination, &theme, &html_config)
|
||||
.chain_err(|| "Unable to copy across static files")?;
|
||||
self.copy_additional_css_and_js(&html_config, &destination)
|
||||
self.copy_additional_css_and_js(&html_config, &ctx.root, &destination)
|
||||
.chain_err(|| "Unable to copy across additional CSS and JS")?;
|
||||
|
||||
// Copy all remaining files
|
||||
@@ -371,11 +376,11 @@ fn make_data(root: &Path, book: &Book, config: &Config, html_config: &HtmlConfig
|
||||
let mut css = Vec::new();
|
||||
for style in &html.additional_css {
|
||||
match style.strip_prefix(root) {
|
||||
Ok(p) => css.push(p.to_str().expect("Could not convert to str")),
|
||||
Ok(p) => {
|
||||
css.push(p.to_str().expect("Could not convert to str"))
|
||||
},
|
||||
Err(_) => {
|
||||
css.push(style.file_name()
|
||||
.expect("File has a file name")
|
||||
.to_str()
|
||||
css.push(style.to_str()
|
||||
.expect("Could not convert to str"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ pub use self::html_handlebars::HtmlHandlebars;
|
||||
mod html_handlebars;
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::io::{self, Read};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use serde_json;
|
||||
@@ -26,6 +26,8 @@ use errors::*;
|
||||
use config::Config;
|
||||
use book::Book;
|
||||
|
||||
const MDBOOK_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// An arbitrary `mdbook` backend.
|
||||
///
|
||||
/// Although it's quite possible for you to import `mdbook` as a library and
|
||||
@@ -68,7 +70,7 @@ pub struct RenderContext {
|
||||
|
||||
impl RenderContext {
|
||||
/// Create a new `RenderContext`.
|
||||
pub(crate) fn new<P, Q>(root: P, book: Book, config: Config, destination: Q) -> RenderContext
|
||||
pub fn new<P, Q>(root: P, book: Book, config: Config, destination: Q) -> RenderContext
|
||||
where
|
||||
P: Into<PathBuf>,
|
||||
Q: Into<PathBuf>,
|
||||
@@ -76,7 +78,7 @@ impl RenderContext {
|
||||
RenderContext {
|
||||
book: book,
|
||||
config: config,
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
version: MDBOOK_VERSION.to_string(),
|
||||
root: root.into(),
|
||||
destination: destination.into(),
|
||||
}
|
||||
@@ -155,13 +157,22 @@ impl Renderer for CmdRenderer {
|
||||
|
||||
let _ = fs::create_dir_all(&ctx.destination);
|
||||
|
||||
let mut child = self.compose_command()?
|
||||
let mut child = match self.compose_command()?
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.current_dir(&ctx.destination)
|
||||
.spawn()
|
||||
.chain_err(|| "Unable to start the renderer")?;
|
||||
.spawn() {
|
||||
Ok(c) => c,
|
||||
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
|
||||
warn!("The command wasn't found, is the \"{}\" backend installed?", self.name);
|
||||
warn!("\tCommand: {}", self.cmd);
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e).chain_err(|| "Unable to start the backend")?;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let mut stdin = child.stdin.take().expect("Child has stdin");
|
||||
|
||||
@@ -340,6 +340,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
background-color: #fafafa;
|
||||
color: #364149;
|
||||
}
|
||||
.light .sidebar::-webkit-scrollbar {
|
||||
background: #fafafa;
|
||||
}
|
||||
.light .sidebar::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
}
|
||||
.light .chapter li {
|
||||
color: #aaa;
|
||||
}
|
||||
@@ -464,6 +470,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
.light .icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
.light ::-webkit-scrollbar {
|
||||
background: #fff;
|
||||
}
|
||||
.light ::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
}
|
||||
.coal {
|
||||
color: #98a3ad;
|
||||
background-color: #141617;
|
||||
@@ -494,6 +506,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
background-color: #292c2f;
|
||||
color: #a1adb8;
|
||||
}
|
||||
.coal .sidebar::-webkit-scrollbar {
|
||||
background: #292c2f;
|
||||
}
|
||||
.coal .sidebar::-webkit-scrollbar-thumb {
|
||||
background: #a1adb8;
|
||||
}
|
||||
.coal .chapter li {
|
||||
color: #505254;
|
||||
}
|
||||
@@ -618,6 +636,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
.coal .icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
.coal ::-webkit-scrollbar {
|
||||
background: #141617;
|
||||
}
|
||||
.coal ::-webkit-scrollbar-thumb {
|
||||
background: #a1adb8;
|
||||
}
|
||||
.navy {
|
||||
color: #bcbdd0;
|
||||
background-color: #161923;
|
||||
@@ -648,6 +672,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
background-color: #282d3f;
|
||||
color: #c8c9db;
|
||||
}
|
||||
.navy .sidebar::-webkit-scrollbar {
|
||||
background: #282d3f;
|
||||
}
|
||||
.navy .sidebar::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
.navy .chapter li {
|
||||
color: #505274;
|
||||
}
|
||||
@@ -772,6 +802,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
.navy .icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
.navy ::-webkit-scrollbar {
|
||||
background: #161923;
|
||||
}
|
||||
.navy ::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
.rust {
|
||||
color: #262625;
|
||||
background-color: #e1e1db;
|
||||
@@ -802,6 +838,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
background-color: #3b2e2a;
|
||||
color: #c8c9db;
|
||||
}
|
||||
.rust .sidebar::-webkit-scrollbar {
|
||||
background: #3b2e2a;
|
||||
}
|
||||
.rust .sidebar::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
.rust .chapter li {
|
||||
color: #505254;
|
||||
}
|
||||
@@ -926,6 +968,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
.rust .icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
.rust ::-webkit-scrollbar {
|
||||
background: #e1e1db;
|
||||
}
|
||||
.rust ::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
.ayu {
|
||||
color: #c5c5c5;
|
||||
background-color: #0f1419;
|
||||
@@ -956,6 +1004,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
background-color: #14191f;
|
||||
color: #c8c9db;
|
||||
}
|
||||
.ayu .sidebar::-webkit-scrollbar {
|
||||
background: #14191f;
|
||||
}
|
||||
.ayu .sidebar::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
.ayu .chapter li {
|
||||
color: #5c6773;
|
||||
}
|
||||
@@ -1080,6 +1134,12 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
.ayu .icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
.ayu ::-webkit-scrollbar {
|
||||
background: #0f1419;
|
||||
}
|
||||
.ayu ::-webkit-scrollbar-thumb {
|
||||
background: #c8c9db;
|
||||
}
|
||||
@media only print {
|
||||
#sidebar,
|
||||
#menu-bar,
|
||||
|
||||
@@ -172,7 +172,7 @@ function playpen_text(playpen) {
|
||||
|
||||
var buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = "<i class=\"fa fa-expand\" title=\"Show hidden lines\"></i>";
|
||||
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||
|
||||
// add expand button
|
||||
pre_block.prepend(buttons);
|
||||
@@ -184,6 +184,7 @@ function playpen_text(playpen) {
|
||||
e.target.classList.remove('fa-expand');
|
||||
e.target.classList.add('fa-compress');
|
||||
e.target.title = 'Hide lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
Array.from(lines).forEach(function (line) {
|
||||
line.classList.remove('hidden');
|
||||
@@ -195,6 +196,7 @@ function playpen_text(playpen) {
|
||||
e.target.classList.remove('fa-compress');
|
||||
e.target.classList.add('fa-expand');
|
||||
e.target.title = 'Show hidden lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
Array.from(lines).forEach(function (line) {
|
||||
line.classList.remove('unhidden');
|
||||
@@ -217,6 +219,7 @@ function playpen_text(playpen) {
|
||||
var clipButton = document.createElement('button');
|
||||
clipButton.className = 'fa fa-copy clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
|
||||
|
||||
buttons.prepend(clipButton);
|
||||
@@ -237,11 +240,13 @@ function playpen_text(playpen) {
|
||||
runCodeButton.className = 'fa fa-play play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
|
||||
var copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.prepend(runCodeButton);
|
||||
buttons.prepend(copyCodeClipboardButton);
|
||||
@@ -255,6 +260,7 @@ function playpen_text(playpen) {
|
||||
var undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'fa fa-history reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
|
||||
buttons.prepend(undoChangesButton);
|
||||
|
||||
@@ -279,6 +285,7 @@ function playpen_text(playpen) {
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
var html = document.querySelector('html');
|
||||
var themeToggleButton = document.getElementById('theme-toggle');
|
||||
var themePopup = document.getElementById('theme-list');
|
||||
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
@@ -331,9 +338,15 @@ function playpen_text(playpen) {
|
||||
});
|
||||
}
|
||||
|
||||
var previousTheme;
|
||||
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||
if (previousTheme === null || previousTheme === undefined) { previousTheme = 'light'; }
|
||||
|
||||
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
|
||||
|
||||
document.body.className = theme;
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
}
|
||||
|
||||
// Set theme
|
||||
@@ -422,7 +435,7 @@ function playpen_text(playpen) {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now()
|
||||
};
|
||||
});
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function (e) {
|
||||
if (!firstContact)
|
||||
@@ -440,7 +453,7 @@ function playpen_text(playpen) {
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
});
|
||||
}, { passive: true });
|
||||
|
||||
// Scroll sidebar to current active section
|
||||
var activeSection = sidebar.querySelector(".active");
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||
document.body.className = theme;
|
||||
document.querySelector('html').className = theme;
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
@@ -88,10 +89,10 @@
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="submenu">
|
||||
@@ -106,7 +107,7 @@
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -130,13 +131,13 @@
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
@@ -148,13 +149,13 @@
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
@@ -9,6 +9,8 @@ $sidebar-non-existant = #5c6773
|
||||
$sidebar-active = #ffb454
|
||||
$sidebar-spacer = #2d334f
|
||||
|
||||
$scrollbar = $sidebar-fg
|
||||
|
||||
$icons = #737480
|
||||
$icons-hover = #b7b9cc
|
||||
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
.sidebar {
|
||||
background-color: $sidebar-bg
|
||||
color: $sidebar-fg
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background: $sidebar-bg;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $scrollbar;
|
||||
}
|
||||
}
|
||||
|
||||
.chapter li {
|
||||
@@ -172,4 +180,12 @@
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
background: $bg;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $scrollbar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ $sidebar-non-existant = #505254
|
||||
$sidebar-active = #3473ad
|
||||
$sidebar-spacer = #393939
|
||||
|
||||
$scrollbar = $sidebar-fg
|
||||
|
||||
$icons = #43484d
|
||||
$icons-hover = #b3c0cc
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ $sidebar-non-existant = #aaaaaa
|
||||
$sidebar-active = #008cff
|
||||
$sidebar-spacer = #f4f4f4
|
||||
|
||||
$scrollbar = #cccccc
|
||||
|
||||
$icons = #cccccc
|
||||
$icons-hover = #333333
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ $sidebar-non-existant = #505274
|
||||
$sidebar-active = #2b79a2
|
||||
$sidebar-spacer = #2d334f
|
||||
|
||||
$scrollbar = $sidebar-fg
|
||||
|
||||
$icons = #737480
|
||||
$icons-hover = #b7b9cc
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ $sidebar-non-existant = #505254
|
||||
$sidebar-active = #e69f67
|
||||
$sidebar-spacer = #45373a
|
||||
|
||||
$scrollbar = $sidebar-fg
|
||||
|
||||
$icons = #737480
|
||||
$icons-hover = #262625
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn take_lines<R: RangeArgument<usize>>(s: &str, range: R) -> String {
|
||||
let start = *range.start().unwrap_or(&0);
|
||||
let mut lines = s.lines().skip(start);
|
||||
match range.end() {
|
||||
Some(&end) => lines.take(end).join("\n"),
|
||||
Some(&end) => lines.take(end.checked_sub(start).unwrap_or(0)).join("\n"),
|
||||
None => lines.join("\n"),
|
||||
}
|
||||
}
|
||||
@@ -62,9 +62,12 @@ mod tests {
|
||||
#[test]
|
||||
fn take_lines_test() {
|
||||
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||
assert_eq!(take_lines(s, 0..3), "Lorem\nipsum\ndolor");
|
||||
assert_eq!(take_lines(s, 1..3), "ipsum\ndolor");
|
||||
assert_eq!(take_lines(s, 3..), "sit\namet");
|
||||
assert_eq!(take_lines(s, ..3), "Lorem\nipsum\ndolor");
|
||||
assert_eq!(take_lines(s, ..), s);
|
||||
// corner cases
|
||||
assert_eq!(take_lines(s, 4..3), "");
|
||||
assert_eq!(take_lines(s, ..100), s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ fn failing_alternate_backend() {
|
||||
md.build().unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_backends_arent_fatal() {
|
||||
let (md, _temp) = dummy_book_with_backend("missing", "trduyvbhijnorgevfuhn");
|
||||
|
||||
assert!(md.build().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alternate_backend_with_arguments() {
|
||||
let (md, _temp) = dummy_book_with_backend("arguments", "echo Hello World!");
|
||||
|
||||
Reference in New Issue
Block a user