注释
这是 mdbook document 的非官方中文翻译版本,根据mdBook document(official) GitHub 版本 0.4.18 翻译
简介
mdBook是一个通过Markdown制作图书的命令行工具软件。它非常适合创建产品说明或API文档、教程、课程教材或任何需要清晰、易于导航和可定制的演示文稿。
- 轻量级的Markdown语法帮助你聚焦写作
- 集成的搜索功能支持
- 支持多种编程语言代码块的语法高亮
- 主题文件允许自定义输出的格式
- 预处理能够为自定义语法和内容修改提供扩展
- 后端可以将输出多种格式文件
- 由高效、安全、简单的Rust语言编写
- 自动对Rust代码示例进行测试
本指南就是由 mdBook 生成的一个示例。同时mdBook也被Rust语言项目使用,而 The Rust Programming Language 这本书也是mdBook生成的另一个很好的示例。
贡献
mdBook是免费的开源的。您可以在 GitHub找到源代码,同时您也可以将问题和功能请求发布在 GitHub issue tracker。mdBook 依靠社区来修复错误和 添加功能:如果您想做出贡献,请阅读 CONTRIBUTE 指南,并考虑是否提交一个 拉取请求。
许可协议
mdBook 源代码及文档基于 Mozilla Public License v2.0发布.
安装
这里由很多途径来安装mdBook命令行工具。您可以选择最适合您的方式进行安装。如果你想通过自动部署的方式来使用 mdbook ,可以参考持续集成的章节更多的示例。
已经编译的可执行程序
可以在GitHub Releases page下载适用于您使用操作系统的可执行文件(Windows,macOS or Linux)以及源代码的压缩文件。
为了方便使用,建议将执行文件的路径添加到您的PATH路径上
通过Rust编译源代码的方式构建
为了通过源代码编译的方式生成mdbook程序,您首先需要安装Rust 程序 和 Cargo.可以参考 Rust installation page.注意 mdbook 需要 Rust 版本在1.54 以上。
安装Rust程序以后,可以通过下面的命令编译及安装 mdBook:
cargo install mdbook
cargo 将自动从 crates.io下载mdBook 源代码,进行编译和安装。生成的可执行文件被生成在 Cargo全局执行目录 (~/.cargo/bin/
默认).
安装最新版本
在 crates.io上发布的mdbook 不是最新版本的(略低于最新版本),如果需要最新版本可以通过Cargo 从 GitHub 构建最新的版本,这是非常容易的,运行下面的命令:
cargo install --git https://github.com/rust-lang/mdBook.git mdbook
在运行cargo前,一定要确认 cargo 可执行路径已经加到您的 PATH 里
如果您有兴趣对 mdBook 本身进行修改,请查看 贡献指南 了解更多信息。
阅读mdBook生成的图书
本章介绍了如何与 mdBook 制作的图书进行交互。假设您正在阅读一本 HTML 书籍。这相对于其他输出格式(如 PDF),选项和格式将有所不同。
电子书是由章节组成的。每章都是一个单独的页面。章节可以嵌套子章节。通常,每个章节将组织成一系列标题以细分一章。
导航
有很多途径可以导航到图书的章节
左侧的侧边栏提供了所有章节的列表。 点击任何章节标题将显示这一章节。
如果窗口太窄,边栏可能不会自动显示,尤其是在移动端上。 在这种情况下,可以按下页面左上角的菜单图标(三个水平条 类似粗体 “三”)以打开和关闭侧边栏。
正文页面底部的箭头按钮可用于导航到上一章或下一章。
键盘上的向左和向右箭头键也可用于导航到上一章或下一章。
顶部菜单条
在页面顶部的菜单条提供了一些图标,用于与图书的交互。下方展示的图标是否显示,取决于图书生成时的制作设定。
图标 | 说明 |
---|---|
打开或关闭显示章节列表的侧边栏。 | |
打开颜色主题列表 | |
打开搜索框,对全书内容进行搜索 | |
打开 浏览器打印预览界面,打印整本书。 | |
打开指向托管图书源代码的网站的链接。 | |
打开一个页面以直接编辑您当前正在阅读的页面的源代码。 |
点击菜单栏(除去图标的地方)会将页面滚动到顶部。
搜索
每本书都有一个内置的搜索系统。 按菜单栏中的搜索图标()或按键盘上的“S”键将打开一个输入框,用于输入搜索词。 键入某些术语将实时显示匹配的章节和部分。
点击任何搜索处的结果都将跳转到该部分。 向上和向下箭头键可用于导航结果,Enter 将打开突出显示的部分。
加载搜索结果后,匹配的搜索词将在文本中突出显示。单击突出显示的单词或按 Esc 键将删除突出显示。
代码块
mdBook 制作的书籍通常用于编程项目,因此它支持突出显示代码块和示例。 代码块可能包含几个不同样式的图标,用于读者与它们进行交互:
Icon | Description |
---|---|
将代码块复制到本地剪贴板中,允许复制粘贴到另一个应用程序中。 | |
对于 Rust 代码示例,点击后将执行示例代码,并在示例下方显示输出结果。(see playground). | |
对于 Rust 代码示例,这将显示或隐藏“隐藏”代码的可见性。有时,较大的示例会隐藏与所说明内容不是特别相关的行。 (参考 hiding code lines). | |
对于 可编辑的代码示例,这将撤消您所做的任何更改。 |
下面时一个简单的例子:
#![allow(unused)] fn main() { println!("Hello, World!"); }
通过mdBook制作图书
一旦您已经安装了mdbook命令行工具,那么您可以使用它创建或渲染您的图书
初始化开始制作图书
mdbook init
命令将创建一个包含空书的新目录,供您开始使用。my-first-book
是为其指定要创建的目录的名称:
mdbook init my-first-book
在生成书籍之前,它会问几个问题.回答完问题后,初始化完成。您可以进入新生成的目录:
cd my-first-book
有几种方法可以渲染一本书,但最简单的方法之一是使用“serve”命令,这将自动构建你的书并启动本地Web服务器:
mdbook serve --open
--open 选项将打开默认的 Web 浏览器以查看新图书。即使在编辑书籍内容时,您也可以让服务器保持运行状态,mdbook将自动重建输出并自动刷新Web浏览器。
查看 CLI Guide 可以获取更多的mdbook
命令和CLI 选项
mdbook 图书构建细节
mdbook制作的图书是由几个文件构建的,这些文件定义了书的设置和布局。
book.toml
在您的创建图书的根目录下,有一个 book.toml
文件,这个文件用来表述如何构建书籍的设置。这是用TOML标记语言编写的。
默认设置通常足以让你入门。
当您有兴趣探索 mdBook 提供的更多功能和选项时,请查看 配置章节 了解更多详情。
一个非常简单的 book.toml
像下面的例子:
[book]
title = "My First Book"
SUMMARY.md
另外一个主要部分是位于 src/SUMMARY.md
的摘要文件。此文件包含本书中所有章节的列表。必须先将某一章添加到此列表中,然后才能查看该章。
下面是一个包含几个章节的基本摘要文件示例:
# Summary
[Introduction](README.md)
- [My First Chapter](my-first-chapter.md)
- [Nested example](nested/README.md)
- [Sub-chapter](nested/sub-chapter.md)
可以尝试用在你的编辑器中打开 src/SUMMARY.md 并添加一些章节。如果任何章节文件不存在,mdbook将自动为您创建它们。
有关摘要文件的其他格式设置选项的更多详细信息,请查看 SUMMARY.md 章节.
源文件
您的图书内容都包含在src
目录中。
每个章节都是一个单独的 Markdown 文件。
通常,每章都以 H1 级标题和章节标题开头。
# My First Chapter
Fill out your content here.
文件的精确布局取决于您。文件的组织将与生成的 HTML 文件相对应,因此请记住,文件布局是每个章节 URL 的一部分。
当 mdbook serve 命令正在运行时,您可以打开任何章节文件并开始编辑它们。每次保存文件时,mdbook都会重建书籍并刷新您的Web浏览器。
查看 Markdown chapter 章节,了解有关设置章节内容格式的更多信息。
src
目录中的所有其他文件都将包含在输出中。因此,如果您有图像或其他静态文件,只需将它们包含在src
目录中的某个位置即可。
发布图书
一旦您写完书后,您可能希望将其托管在某个地方供其他人查看。
第一步是建立这本书的输出。
这可以通过book.toml
文件所在的同一目录中的mdbook build
命令来完成:
mdbook build
这将生成一个名为book
的目录,其中包含您图书的全部HTML内容。
然后,您可以将此目录book
里面的内容放在任何 Web 服务器上以托管它。
有关发布和部署的详细信息,请查看持续集成章节 了解更多信息。
Command Line Tool 命令行工具
mdbook
命令行工具用于创建和构建书籍。安装 mdbook 后,可以在终端中运行 mdbook
帮助命令以查看可用的命令。
以下各节提供有关不同命令的详细解释。
mdbook init <directory>
— 初始化新书,并提供简单的模板mdbook build
— 生成图书.mdbook watch
— 每当源文件发生更改时,重新生成图书。mdbook serve
— Runs a web server to view the book, and rebuilds on changes.mdbook test
— Tests Rust code samples.mdbook clean
— Deletes the rendered output.mdbook completions
— Support for shell auto-completion.
The init command 初始化命令
每本新书都有共性的样板,因此 mdbook 提供了 init
命令
init
像下面这样:
mdbook init
首次使用init
命令时,将自动生成设置几个文件和文件夹
book-test/
├── book
└── src
├── chapter_1.md
└── SUMMARY.md
src
目录是你用markdown写书的地方。它包含所有源文件,配置文件等。book
目录是图书的生成呈现位置。所有输出内容可以 上传到服务器,供您的读者阅读。SUMMARY.md
规划您图书的结构,并更详细地讨论在另一 章.
提示:从 SUMMARY.md 生成章节
当SUMMARY.md
文件已存在时,init
命令将首先解析它
并根据SUMMARY.md
中使用的路径生成丢失或者不存在的文件。
这使您可以思考并创建书籍的整个结构,然后
让 mdBook 为您生成它。
指定目录
init
命令可以将目录作为参数,以用作书籍的根目录
而不是当前的工作目录。
mdbook init path/to/book
--theme
当您使用--theme
参数时,默认主题将被复制到
源目录中名为theme
的目录,以便您可以对其进行修改。
主题被选择性地覆盖,这意味着如果你不想 覆盖特定文件,只需将其删除,即可使用默认文件。
--title
指定图书的标题。如果未提供,交互式提示将要求输入 标题。
mdbook init --title="my amazing book"
--ignore
创建一个“.gitignore”文件,该文件配置为忽略building书籍时创建的“书籍”目录。 如果未提供,将出现一个交互式提示,询问是否应创建它。
The build command 构建命令
build 命令用于渲染您的图书:
mdbook build
它将尝试解析您的SUMMARY.md
文件,以了解书的结构
并据此获取相应的文件。请注意,SUMMARY.md
中提到的文件如果不存此
是会自动创建的。
为方便起见,输出结果将保持与源相同的目录结构。因此,大本书籍在渲染时将保持结构化。
指定目录
build
命令可以将指定目录作为参数,用来告知build
书籍的根目录,而不是当前工作目录。
mdbook build path/to/book
--open
当您使用--open
(-o
)标志时,mdbook将在
构建后,打开默认Web浏览器显示生成效果。
--dest-dir
--dest-dir
(-d
) 选项,允许您改变您的图书输出路径。注意如果是相对路径,则相对的是图书的根目录。如果没有指定 --dest-dir
(-d
),则 --dest-dir
(-d
)的路径是在book.toml
指定的,一般是 ./book
注意: *build 命令将从源目录中复制所有文件(但不包括扩展名为 “.md” 的文件) 到构建目录中。
The watch command 监控命令
当您希望实时在图书目录下的任何一个文件发生变化时,可以自动重新构建的的话,watch
命令是非常有效的。这样您可以不必重复的使用 mdbook build
命令。另外,在因为文件发生变化触发重建的时候,同样会重建在 SUMMARY.md
记录但实际上不存在的文件
指定目录
watch
命令可以将目录作为参数用于指定书籍的
根目录而不是当前工作目录。
mdbook watch path/to/book
--open
当您使用--open
(-o
)选项时,mdbook将在重构后,打开
您的默认网络浏览器。
--dest-dir
--dest-dir
(-d
) 选项,允许您改变您的图书输出路径。注意如果是相对路径,则相对的是图书的根目录。如果没有指定 --dest-dir
(-d
),则 --dest-dir
(-d
)的路径是在book.toml
指定的,一般是 ./book
Specify exclude patterns 指定排除模式
watch
命令不会自动触发
书籍根目录中.gitignore
文件中包含的文件。.gitignore
文件可以参考 gitignore
documentation.。这对于
忽略某些编辑者创建的临时文件是有帮助的。
注意:只有在图书根目录的.gitignore
是有效的。全局变量指定的$HOME/.gitignore
或者上一级目录的.gitignore
文件都是无效的。
The serve command
serve 命令可以在本地建立一个web服务器 通过 localhost:3000
访问预览你的图书
mdbook serve
serve
命令将监控书籍src
目录
内容的更改,随时重建书籍并为每个更改刷新客户端;这包括
重新创建SUMMARY.md
中仍然提到的已删除文件!并通过 websocket
连接用于触发客户端刷新。
注意: serve
命令用于测试书籍的 HTML 输出,而不是
旨在成为网站的完整HTTP服务器。
指定目录
serve
命令可以指定目录作为参数用作书籍
根目录而不是当前工作目录。
mdbook serve path/to/book
Server options
serve
主机名默认为“localhost”,端口默认为“3000”。可以在命令行上指定任一选项:
mdbook serve path/to/book -p 8000 -n 127.0.0.1
--open
当您使用--open
(-o
)选项时,mdbook将在重构后,打开
您的默认网络浏览器。
--dest-dir
--dest-dir
(-d
) 选项,允许您改变您的图书输出路径。注意如果是相对路径,则相对的是图书的根目录。如果没有指定 --dest-dir
(-d
),则 --dest-dir
(-d
)的路径是在book.toml
指定的,一般是 ./book
Specify exclude patterns
serve
命令不会自动触发
书籍根目录中.gitignore
文件中包含的文件。.gitignore
文件可以参考 gitignore
documentation.。这对于
忽略某些编辑者创建的临时文件是有帮助的。
注意:只有在图书根目录的.gitignore
是有效的。全局变量指定的$HOME/.gitignore
或者上一级目录的.gitignore
文件都是无效的。
The test command
在写一本书时,你有时需要自动化一些测试。例如 Rust Programming Book有很多 可能会过时的代码示例。因此,自动测试这些代码示例也是非常重要的。
mdBook 支持test
命令,该命令将运行书籍中的所有可用测试。注意在
目前,仅支持 rustdoc 测试,但未来可能会支持的更多。
禁止对代码块进行测试
rustdoc 不会测试包含 ignore
属性的代码块:
```rust,ignore
fn main() {}
```
rustdoc 也不会测试指定 Rust 以外语言的代码块:
```markdown
**Foo**: _bar_
```
rustdoc 也不会测试未指定语言的代码块:
```
This is going to cause an error!
```
指定目录
test
命令可以指定目录作为参数用作书籍
根目录而不是当前工作目录。
mdbook test path/to/book
--library-path
--library-path
(-L
) 选项允许指定目录用于
rustdoc
在构建和测试示例时使用的库搜索路径。添加多个
目录可以使用多个选项((-L foo -L bar
)或
逗号分隔列表 (-L foo,bar)。路径应指向Cargo
build cache deps
目录
包含项目的生成输出。例如,如果你的 Rust 项目的书在目录中 名为“my-book”,以下命令将在运行“test”时包含 crate的依赖项:
mdbook test my-book -L target/debug/deps/
获取更多信息,可以参考 rustdoc
命令行 documentation
--dest-dir
--dest-dir
(-d
) 选项,允许您改变您的图书输出路径。注意如果是相对路径,则相对的是图书的根目录。如果没有指定 --dest-dir
(-d
),则 --dest-dir
(-d
)的路径是在book.toml
指定的,一般是 ./book
The clean command
clean 命令用于清除生成的书籍和任何其他构建效果。
mdbook clean
指定目录
clean
命令可以指定目录作为参数用作书籍
根目录而不是当前工作目录。
mdbook clean path/to/book
--dest-dir
--dest-dir
(-d
) 选项,允许您改变您的图书输出路径。注意如果是相对路径,则相对的是图书的根目录。如果没有指定 --dest-dir
(-d
),则 --dest-dir
(-d
)的路径是在book.toml
指定的,一般是 ./book
mdbook clean --dest-dir=path/to/book
path/to/book
可以是相对路径也可以是绝对路径
The completions command
completions 命令用于为某些常见 shell 生成自动完成。这意味着当您在 shell 中键入 mdbook
时,您可以按 shell 的自动完成键(通常是 Tab 键),它可能会显示有效选项是什么,或者完成部分输入。
首先需要为您的 shell 安装完成:
mdbook completions bash > ~/.local/share/bash-completion/completions/mdbook
这个命令会告诉指定shell可以自动补全的脚本
运行 mdbook completions --help
以获取支持的shell列表。
将自动补全项放置在何处取决于您使用的 shell 和操作系统。 请参阅 shell 的文档,了解在何处放置脚本的详细信息。
Format
在本节中,您将学习如何::
- 正确构建图书
- 格式化
SUMMARY.md
文件 - 使用
book.toml
配置您的图书 - 自定义您的主题
SUMMARY.md 摘要文件
mdBook 使用摘要文件来了解要包含的章节、它们应以何种顺序显示、其层次结构是什么以及源文件的位置。没有这个summary.md
摘要文件,就没有书。
这个命名为SUMMARY.md
格式为markdown的文件。其格式有要求
非常严格,必须遵循下面概述的结构,以便
解析。未指定的任何元素,无论是格式还是文本,都可能
被忽略,或者在尝试构建书籍时可能导致错误。
Structure 结构
-
Title - 虽然是可选的,但通常的做法是从标题开始,通常是
# Summary
. 但是,解析器会忽略这一点,并且 可以省略。# Summary
-
Prefix Chapter - 前缀章节。在主要编号章节之前,可以添加前缀章节,这些是不会被编号。这对前言、简介等很有用。当然也存在一些制约因素。前缀章节不能是 嵌套;它们都应该在根级别上。并且您无法在 添加编号章节后添加前缀章节。
[A Prefix Chapter](relative/path/to/markdown.md) - [First Chapter](relative/path/to/markdown2.md)
-
Part Title - 分割标题。Headers can be used as a title for the following numbered chapters. This can be used to logically separate different sections of the book. The title is rendered as unclickable text. Titles are optional, and the numbered chapters can be broken into as many parts as desired.在一组编号的章节上面可以添加标题作为逻辑分割标题。标题呈现为不可点击的文本。 标题是可选的,编号的章节可以分成尽可能多的章节。
# My Part Title - [First Chapter](relative/path/to/markdown.md)
-
Numbered Chapter - 编号章节概述了本书的主要内容 并且可以嵌套,从而产生一个很好的层次结构 (章节、子章节等)。
# Title of Part - [First Chapter](relative/path/to/markdown.md) - [Second Chapter](relative/path/to/markdown2.md) - [Sub Chapter](relative/path/to/markdown3.md) # Title of Another Part - [Another Chapter](relative/path/to/markdown4.md)
编号的章节可以用
-
或*
表示(不要混合分隔符)。 -
Suffix Chapter - 后缀章节.与前缀章节一样,后缀章节未编号,但它们位于后面 编号的章节。
- [Last Chapter](relative/path/to/markdown.md) [Title of Suffix Chapter](relative/path/to/markdown2.md)
-
Draft chapters - 草稿章节是没有文件,因此没有内容的章节。 章节草案的目的是表明未来的章节仍有待编写。 或者,当仍然布置书籍的结构以避免创建文件时 而你仍然在改变这本书的结构。 草稿章节将在 HTML 呈现器中呈现为表格中的禁用链接 的内容,如您在左侧目录中的下一章中看到的那样。 草稿章节的编写方式与普通章节类似,但未写入文件的路径。
- [Draft Chapter]()
-
Separators - 可以在任何其他元素之前、之间和之后添加分隔符。他们的结果 在构建的目录中的 HTML 呈现行中。 分隔符是 一行仅包含破折号和至少三个破折号:
---
。# My Part Title [A Prefix Chapter](relative/path/to/markdown.md) --- - [First Chapter](relative/path/to/markdown2.md)
Example 例子
以下是本指南的SUMMARY.md
的markdown 源码,左侧的栏位显示的内容就是设置的结果。
# Summary
[简介](README.md)
# 使用指南
- [安装](guide/installation.md)
- [阅读mdBook生成的图书](guide/reading.md)
- [通过mdBook制作图书](guide/creating.md)
# 参考指南
- [命令行工具](cli/README.md)
- [init](cli/init.md)
- [build](cli/build.md)
- [watch](cli/watch.md)
- [serve](cli/serve.md)
- [test](cli/test.md)
- [clean](cli/clean.md)
- [completions](cli/completions.md)
- [Format](format/README.md)
- [SUMMARY.md](format/summary.md)
- [Draft chapter]()
- [配置](format/configuration/README.md)
- [General](format/configuration/general.md)
- [Preprocessors](format/configuration/preprocessors.md)
- [Renderers](format/configuration/renderers.md)
- [Environment Variables](format/configuration/environment-variables.md)
- [Theme](format/theme/README.md)
- [index.hbs](format/theme/index-hbs.md)
- [Syntax highlighting](format/theme/syntax-highlighting.md)
- [Editor](format/theme/editor.md)
- [MathJax Support](format/mathjax.md)
- [mdBook-specific features](format/mdbook.md)
- [Markdown](format/markdown.md)
- [持续集成](continuous-integration.md)
- [For Developers](for_developers/README.md)
- [Preprocessors](for_developers/preprocessors.md)
- [Alternative Backends](for_developers/backends.md)
-----------
[Contributors](misc/contributors.md)
配置
本节详细介绍了 book.toml 中可用的配置选项:
- General 配置包括
书
,rust
,build
部分 - Preprocessor 默认和自定义书籍预处理器的配置
- Renderer HTML、Markdown 和自定义渲染器的配置
- Environment Variable 用于覆盖环境中的配置选项的配置
General Configuration
您可以在 book.toml 文件中配置图书的参数。
下面是一个示例,说明 book.toml 文件:
[book]
title = "Example book"
author = "John Doe"
description = "The example book covers examples."
[rust]
edition = "2018"
[build]
build-dir = "my-example-book"
create-missing = false
[preprocessor.index]
[preprocessor.links]
[output.html]
additional-css = ["custom.css"]
[output.html.search]
limit-results = 15
支持的配置选项
请务必注意,在 配置中的路径始终相对于书的根的位置,即配置文件的 位置。
常规元数据
下面是图书的通常信息.
- title: 书名
- authors: 本书的作者
- description: 该书的说明,作为元数据添加
每个页面的 html的
<head>
中 - src: 默认情况下,源目录位于
根目录的
src
目录。但这可以通过src
元数据进行设置。 - language: 本书的主要语言,例如用作语言属性
<html lang="en">
。
book.toml
[book]
title = "Example book"
authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples."
src = "my-src" #源文件将放在“root/my-src”中,而不是“root/src”
language = "en"
Rust options Rust 选项
Options for the Rust language, relevant to running tests and playground integration.
Rust 语言的选项,与运行测试和 Playground 集成 有关。
[rust]
edition = "2015" # the default edition for code blocks
-
edition: 定义Rust 代码片段的默认版本。默认 是“2015”。单个代码块可以使用注释 如
edition2015
、edition2018
或edition2021
进行设置,例如:```rust,edition2015 // This only works in 2015. let try = true; ```
Build options 构建选项
控制图书的构建过程选项。
[build]
build-dir = "book" # 设置输出的目录
create-missing = true # 是否创建缺失页面
use-default-preprocessors = true # 使用默认预处理器
-
build-dir: 用于放置渲染的书籍的目录。默认情况下,这是
book/
在书籍的根目录中。 这可以用--dest-dir
CLI 选项覆盖。 -
create-missing: 默认情况下,在
SUMMARY.md
中指定的不存在的文件 将在本书构建时创建(即create-missing = true
)。如果 为false
,则当任何一个文件不存在的时候,构建过程将退出并显示错误。 -
use-default-preprocessors: 禁用 默认预处理器(
links
&索引
),通过将此选项设置为false
。如果您有相同的和/或其他预处理器通过其表声明 的配置,它们将改为运行。
- 为清楚起见,在没有定义预处理器配置的情况下,将采用默认的
links
andindex
。 - 设置
use-default-preprocessors = false
则禁止默认的预处理器运行. - 如果添加
[preprocessor.links]
,那么无论是否设置use-default-preprocessors
,它将links
它将运行。
- 为清楚起见,在没有定义预处理器配置的情况下,将采用默认的
Configuring Preprocessors 配置预处理器
Preprocessors are extensions that can modify the raw Markdown source before it gets sent to the renderer.
预处理器是在将原始 Markdown 源发送到渲染器之前对其进行修改的扩展。
The following preprocessors are built-in and included by default:
默认情况下,以下预处理器是内置的:
links
: Expands the{{ #playground }}
,{{ #include }}
, and{{ #rustdoc_include }}
handlebars helpers in a chapter to include the contents of a file. See Including files for more.- 扩展
{{ #playground }}
、{{ #include }}
和{{ #rustdoc_include }}
章节中的帮助程序,用于包含文件的内容。 有关详细信息,请参阅Including files。 index
: Convert all chapter files namedREADME.md
intoindex.md
. That is to say, allREADME.md
would be rendered to an index fileindex.html
in the rendered book.- 将所有名为
README.md
的章节文件转换为index.md
。也就是说,书中所有README.md
都将渲染成索引文件index.html
。
The built-in preprocessors can be disabled with the build.use-default-preprocessors
config option.
可以使用 build.use-default-preprocessors
配置选项禁用内置预处理器。
The community has developed several preprocessors. See the Third Party Plugins wiki page for a list of available preprocessors.
社区已经开发了几个预处理器。 请参阅Third Party Plugins wiki 页面,了解可用的预处理器列表。
For information on how to create a new preprocessor, see the Preprocessors for Developers chapter.
有关如何创建新的预处理器的信息,请参阅Preprocessors for Developers 一章。
Custom Preprocessor Configuration 自定义预处理器配置
Preprocessors can be added by including a preprocessor
table in book.toml
with the name of the preprocessor.
For example, if you have a preprocessor called mdbook-example
, then you can include it with:
可以通过在book.toml
中包含带有预处理器名称的preprocessor
表来添加预处理器。
例如,如果您有一个名为mdbook-example
的预处理器,则可以将其包含在以下位置:
[preprocessor.example]
With this table, mdBook will execute the mdbook-example
preprocessor.
有了这个表,mdBook将执行mdbook-example
预处理器。
This table can include additional key-value pairs that are specific to the preprocessor. For example, if our example preprocessor needed some extra configuration options:
此表可以包含特定于预处理器的其他键值对。 例如,如果我们的示例 preprocessor 需要一些额外的配置选项:
[preprocessor.example]
some-extra-feature = true
Locking a Preprocessor dependency to a renderer将预处理器依赖项锁定到呈现器
You can explicitly specify that a preprocessor should run for a renderer by binding the two together.
您可以通过以下方式显式指定预处理器应为呈现器运行 将两者绑定在一起。
[preprocessor.example]
renderers = ["html"] # 示例预处理器仅使用 HTML 呈现器运行
Provide Your Own Command 提供您自己的命令
By default when you add a [preprocessor.foo]
table to your book.toml
file,
mdbook
will try to invoke the mdbook-foo
executable. If you want to use a
different program name or pass in command-line arguments, this behaviour can
be overridden by adding a command
field.
默认情况下,当您将[preprocessor.foo]
表添加到book.toml
文件时,
mdbook
将尝试调用mdbook-foo
可执行文件。如果要使用
不同的程序名称或传入命令行参数,此行为可以
通过添加 command
字段进行覆盖。
[preprocessor.random]
command = "python random.py"
Require A Certain Order 需要一定的顺序
The order in which preprocessors are run can be controlled with the before
and after
fields.
For example, suppose you want your linenos
preprocessor to process lines that may have been {{#include}}
d; then you want it to run after the built-in links
preprocessor, which you can require using either the before
or after
field:
预处理器的运行顺序可以通过before
和after
字段进行控制。
例如,假设您希望linenos
预处理器处理可能是{{#include}}
的行;然后,您希望它在内置的“链接”预处理器之后运行,您可以要求使用 before
或after
字段:
[preprocessor.linenos]
after = [ "links" ]
or
[preprocessor.links]
before = [ "linenos" ]
It would also be possible, though redundant, to specify both of the above in the same config file.
尽管是多余的,但也可以在同一配置文件中指定上述两者。
Preprocessors having the same priority specified through before
and after
are sorted by name.
Any infinite loops will be detected and produce an error.
通过before
和 after
指定了相同优先级的预处理器按名称排序。
将检测到任何无限循环并产生错误。
Configuring Renderers 配置渲染器
渲染器(也称为“后端”)负责创建书籍的输出。
以下是内置的后端:
html
— 这会将书籍呈现为 HTML。 如果book.toml
中未定义其他[output]
表,则默认启用此功能。markdown
—这将在运行预处理器后将书籍输出为markdown。 这对于调试预处理器非常有用。
社区已经开发了几个后端。 请参阅 Third Party Plugins wiki页面以获取可用后端的列表。
有关如何创建新的后端的信息,请参阅 Backends for Developers 一章。
Output tables
可以通过在 book.toml
中包含带有后端名称的output
表来添加后端。
例如,如果你有一个名为mdbook-wordcount
的后端,那么你可以用:
[output.wordcount]
有了这个表,mdBook将执行mdbook-wordcount
后端。
此表可以包含特定于后端的其他键值对。 例如,如果我们的示例后端需要一些额外的配置选项:
[output.wordcount]
ignores = ["Example Chapter"]
如果定义任何 [output]
表,则默认情况下不启用html
后端。
如果你想保持html
后端运行,那么只需将其包含在book.toml
文件中。
例如:
[book]
title = "My Awesome Book"
[output.wordcount]
[output.html]
如果包含多个输出
表,则会更改输出目录布局的行为。
如果只有一个后端,则将其输出直接放在book
目录中(请参阅build.build-dir
以覆盖此位置)。
如果有多个后端,则每个后端都放在book
下的单独目录中。
例如,上面将有目录book/html
和book/wordcount
。
Custom backend commands 自定义后端命令
默认情况下,当您将 [output.foo]
表添加到book.toml
文件时,
mdbook
将尝试调用mdbook-foo
可执行文件。
如果要使用其他程序名称或传入命令行参数,
可以通过添加command
字段来覆盖此行为。
[output.random]
command = "python random.py"
Optional backends 可选后端
如果启用未安装的后端,则默认行为是引发错误。 可以通过将后端标记为可选来更改此行为:
[output.wordcount]
optional = true
这会将错误降级为警告
HTML renderer options. HTML 呈现器选项
HTML 呈现器具有下面详细介绍的各种选项。
它们应该在book.toml
文件的[output.html]
表中指定。
# 包含所有输出选项的示例 book.toml 文件。
[book]
title = "Example book"
authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples."
[output.html]
theme = "my-theme"
default-theme = "light"
preferred-dark-theme = "navy"
curly-quotes = true
mathjax-support = false
copy-fonts = true
additional-css = ["custom.css", "custom2.css"]
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"
以下可用配置选项:
- theme: mdBook附带了默认主题和所需的所有资源文件 为它。但是,如果设置了此选项,mdBook将有选择地覆盖主题 文件,其中包含在指定文件夹中找到的文件。
- default-theme: 默认情况下要在 “更改主题”下拉列表。默认为“浅”。
- preferred-dark-theme: 默认的深色主题。在以下情况下将使用本主题:
浏览器通过
'prefers-color-scheme'
CSS 媒体查询。默认为
navy
. - curly-quotes:
false
.将直引号转换为卷引号,但那些 出现在代码块和代码中。默认值为false
。 - mathjax-support: 添加了对 MathJax 支持。默认值为
false
。 - copy-fonts: 将字体.css和相应的字体文件复制到输出目录,并在默认主题中使用它们。默认为
true
。 - google-analytics: 此字段已被弃用,并将在将来的版本中删除。
使用
theme/head.hbs
文件来添加相应的Google Analytics(分析)代码。 - additional-css: 如果您需要稍微更改图书的外观 在不覆盖整个样式的情况下,您可以指定一组样式表, 将在默认的之后加载,您可以在其中更改 风格。
- additional-js: 如果您需要在书中添加一些行为,而无需 删除当前行为,您可以指定一组 JavaScript 文件 将与默认一起加载。
- no-section-label: 默认情况下,mdBook 会添加
内容列。例如,“1.”、“2.1”。将此选项设置为 true 可禁用
那些标签。默认值为
false
. - git-repository-url: 书籍的 git 存储库的 url。如果提供 图标链接将输出到书籍的菜单栏中。
- git-repository-icon: 用于 git 的 FontAwesome 图标类
存储库链接。默认为
fa-github
,看起来像 . 如果你没有使用GitHub,另一个要考虑的选项是fa-code-fork
,它看起来像.。 - edit-url-template: 编辑网址模板(如果提供)显示
“建议编辑”按钮(看起来像)用于直接跳转到编辑当前
已查看页面。例如,对于 GitHub 项目,将其设置为
https://github.com/<owner>/<repo>/edit/master/{path}
或 for Bitbucket 项目将其设置为https://bitbucket.org/<owner>/<repo>/src/master/{path}?mode=edit
其中 {path} 将被替换为 存储 库。 - input-404: 用于不存在的 markdown 文件的名称。
相应的输出文件将相同,扩展名将替换为“html”。
默认值为
404.md
。 - site-url: 将托管图书的网址。这是确保
404 文件中的导航链接和脚本/css 导入可以正常工作,即使在访问
子目录中的网址。默认值为
/
- cname: 将托管图书的 DNS 子域或顶点域。 此字符串将写入站点根目录中名为 CNAME 的文件中,如下所示 GitHub Pages 所需的 (see Managing a custom domain for your GitHub Pages site).
[output.html.print]
[output.html.print]
表提供了用于控制可打印输出的选项。
默认情况下,mdBook 将在书籍的右上角包含一个图标(看起来像 ),该图标会将书籍打印为一页。
[output.html.print]
enable = true # include support for printable output
page-break = true # insert page-break after each chapter
- enable: 启用打印支持。当
false
时,所有打印支持将不会 呈现。默认为true
。 - page-break 在章节之间插入分页符。默认为
true
。
[output.html.fold]
The [output.html.fold]
表提供了用于控制导航侧栏中章节列表折叠的选项。
[output.html.fold]
enable = false #是否启用截面折叠
level = 0 # 开始折叠的深度
- enable: 启用截面折叠。关闭时,所有折叠都打开。
默认值为
false
。 - level: 折叠区域越高,打开的区域就越多。当级别为 0 时,全部
褶皱是闭合的。默认值为
0
。
[output.html.playground]
[output.html.playground]
提供了用于控制 Rust 示例代码块及其与 Rust Playground 集成的选项。
[output.html.playground]
editable = false # 允许编辑源代码
copyable = true # 包括用于复制代码片段的复制按钮
copy-js = true # 包括代码编辑器的 JavaScript
line-numbers = false # 显示可编辑代码的行号
runnable = true # 显示Rust 代码的运行按钮
- editable: 允许编辑源代码。默认值为
false
. - copyable: 在代码段上显示复制按钮。默认值为
true
. - copy-js: 将编辑器的 JavaScript 文件复制到输出目录。
默认值为
true
. - line-numbers 在可编辑的代码段上显示行号。要求
editable
和copy-js
都是true
。默认值为 - runnable 显示 Rust 代码段的运行按钮。将此更改为
false
将全局禁用在操场中运行功能。默认值为true
.
[output.html.search]
[output.html.search]
表提供了用于控制内置文本 search的选项。
mdBook 必须在启用search
功能的情况下进行编译(默认情况下处于打开状态)。
[output.html.search]
enable = true # 启用搜索功能
limit-results = 30 # 最大搜索结果数
teaser-word-count = 30 # 用于搜索结果摘要的字数
use-boolean-and = true # 多个搜索词必须全部匹配
boost-title = 2 # 标题中匹配项的排名提升因子
boost-hierarchy = 1 # 页面名称中匹配项的排名提升因素
boost-paragraph = 1 # 文本中匹配项的排名提升因素
expand = true # 部分字词将与较长的字词匹配
heading-split-level = 3 # 将结果链接到标题级别
copy-js = true # 包括用于搜索的 Javascript 代码
- enable: 启用搜索功能。默认值为
true
. - limit-results: 搜索结果的最大数量。默认值为
30
. - teaser-word-count: 用于搜索结果摘要的字数。
默认值为
30
. - use-boolean-and: 定义多个搜索词之间的逻辑链接。如果
true,所有搜索词都必须出现在每个结果中。默认值为
false
. - boost-title: 如果搜索词,则搜索结果分数的提升因素
将显示在页眉中。默认值为
2
. - boost-hierarchy: 如果搜索词,则搜索结果分数的提升因素
将显示在层次结构中。层次结构包含父级的所有标题
文档和所有父标题。默认值为
1
. - boost-paragraph: 如果搜索词,则搜索结果分数的提升因素
将显示在文本中。默认值为
1
. - expand: 如果搜索应匹配较长的结果,例如搜索
micro
,则为 true 应该匹配microwave
。默认为“true”。 - heading-split-level: 搜索结果将链接到文档的某个部分
其中包含结果。文档按标题分成几个部分
级别或更低。默认值为
3
. (### This is a level 3 heading
) - copy-js: 将搜索实现的 JavaScript 文件复制到输出
目录。默认值为
true
.
[output.html.redirect]
[output.html.redirect]
表提供了一种添加重定向的方法。
当您移动、重命名或删除页面以确保指向旧 URL 的链接将转到新位置时,这很有用。
[output.html.redirect]
"/appendices/bibliography.html" = "https://rustc-dev-guide.rust-lang.org/appendix/bibliography.html"
"/other-installation-methods.html" = "../infra/other-installation-methods.html"
该表包含键值对,其中键是需要创建重定向文件的位置,作为构建目录的绝对路径(例如/appendices/bibliography.html
)。
该值可以是浏览器应导航到的任何有效 URI(例如,https://rust-lang.org/
,/overview.html
或../bibliography.html
)。
这将生成一个HTML页面,该页面将自动重定向到给定位置。
请注意,源位置不支持#
锚点重定向。
Markdown Renderer
Markdown 渲染器将运行预处理器,然后输出结果
Markdown。这对于调试预处理器非常有用,尤其是在
与mdbook test
结合使用,以查看mdbook
正在通过的Markdown
到rustdoc
。
Markdown 渲染器包含在 mdbook
中,但默认情况下处于禁用状态。
通过向book.toml
添加一个空表来启用它,如下所示:
[output.markdown]
目前没有Markdown渲染器的配置选项。 仅启用还是禁用。
参考 the preprocessors documentation 有关如何操作 指定哪些预处理器应在 Markdown 渲染器之前运行。
Environment Variables
All configuration values can be overridden from the command line by setting the
corresponding environment variable. Because many operating systems restrict
environment variables to be alphanumeric characters or _
, the configuration
key needs to be formatted slightly differently to the normal foo.bar.baz
form.
Variables starting with MDBOOK_
are used for configuration. The key is created
by removing the MDBOOK_
prefix and turning the resulting string into
kebab-case
. Double underscores (__
) separate nested keys, while a single
underscore (_
) is replaced with a dash (-
).
For example:
MDBOOK_foo
->foo
MDBOOK_FOO
->foo
MDBOOK_FOO__BAR
->foo.bar
MDBOOK_FOO_BAR
->foo-bar
MDBOOK_FOO_bar__baz
->foo-bar.baz
So by setting the MDBOOK_BOOK__TITLE
environment variable you can override the
book's title without needing to touch your book.toml
.
Note: To facilitate setting more complex config items, the value of an environment variable is first parsed as JSON, falling back to a string if the parse fails.
This means, if you so desired, you could override all book metadata when building the book with something like
$ export MDBOOK_BOOK="{'title': 'My Awesome Book', authors: ['Michael-F-Bryan']}" $ mdbook build
The latter case may be useful in situations where mdbook
is invoked from a
script or CI, where it sometimes isn't possible to update the book.toml
before
building.
Theme
The default renderer uses a handlebars template to render your markdown files and comes with a default theme included in the mdBook binary.
The theme is totally customizable, you can selectively replace every file from
the theme by your own by adding a theme
directory next to src
folder in your
project root. Create a new file with the name of the file you want to override
and now that file will be used instead of the default file.
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.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, you should not need to modify this.
- highlight.css is the theme used for the code highlighting.
- favicon.svg and favicon.png the favicon that will be used. The SVG version is used by newer browsers.
Generally, when you want to tweak the theme, you don't need to override all the files. If you only need changes in the stylesheet, there is no point in overriding all the other files. Because custom files take precedence over built-in ones, they will not get updated with new fixes / features.
Note: When you override a file, it is possible that you break some
functionality. Therefore I recommend to use the file from the default theme as
template and only add / modify what you need. You can copy the default theme
into your source directory automatically by using mdbook init --theme
and just
remove the files you don't want to override.
mdbook init --theme
will not create every file listed above.
Some files, such as head.hbs
, do not have built-in equivalents.
Just create the file if you need it.
If you completely replace all built-in themes, be sure to also set
output.html.preferred-dark-theme
in the config, which defaults to the
built-in navy
theme.
index.hbs
index.hbs
is the handlebars template that is used to render the book. The
markdown files are processed to html and then injected in that template.
If you want to change the layout or style of your book, chances are that you will have to modify this template a little bit. Here is what you need to know.
Data
A lot of data is exposed to the handlebars template with the "context". In the handlebars template you can access this information by using
{{name_of_property}}
Here is a list of the properties that are exposed:
-
language Language of the book in the form
en
, as specified inbook.toml
(if not specified, defaults toen
). To use in<html lang="{{ language }}">
for example. -
title Title used for the current page. This is identical to
{{ chapter_title }} - {{ book_title }}
unlessbook_title
is not set in which case it just defaults to thechapter_title
. -
book_title Title of the book, as specified in
book.toml
-
chapter_title Title of the current chapter, as listed in
SUMMARY.md
-
path Relative path to the original markdown file from the source directory
-
content This is the rendered markdown.
-
path_to_root This is a path containing exclusively
../
's that points to the root of the book from the current file. Since the original directory structure is maintained, it is useful to prepend relative links with thispath_to_root
. -
chapters Is an array of dictionaries of the form
{"section": "1.2.1", "name": "name of this chapter", "path": "dir/markdown.md"}
containing all the chapters of the book. It is used for example to construct the table of contents (sidebar).
Handlebars Helpers
In addition to the properties you can access, there are some handlebars helpers at your disposal.
1. toc
The toc helper is used like this
{{#toc}}{{/toc}}
and outputs something that looks like this, depending on the structure of your book
<ul class="chapter">
<li><a href="link/to/file.html">Some chapter</a></li>
<li>
<ul class="section">
<li><a href="link/to/other_file.html">Some other Chapter</a></li>
</ul>
</li>
</ul>
If you would like to make a toc with another structure, you have access to the chapters property containing all the data. The only limitation at the moment is that you would have to do it with JavaScript instead of with a handlebars helper.
<script>
var chapters = {{chapters}};
// Processing here
</script>
2. previous / next
The previous and next helpers expose a link
and name
property to the
previous and next chapters.
They are used like this
{{#previous}}
<a href="{{link}}" class="nav-chapters previous">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
The inner html will only be rendered if the previous / next chapter exists. Of course the inner html can be changed to your liking.
If you would like other properties or helpers exposed, please create a new issue
Syntax Highlighting
mdBook uses Highlight.js with a custom theme for syntax highlighting.
Automatic language detection has been turned off, so you will probably want to specify the programming language you use like this:
```rust
fn main() {
// Some code
}
```
Supported languages
These languages are supported by default, but you can add more by supplying
your own highlight.js
file:
- apache
- armasm
- bash
- c
- coffeescript
- cpp
- csharp
- css
- d
- diff
- go
- handlebars
- haskell
- http
- ini
- java
- javascript
- json
- julia
- kotlin
- less
- lua
- makefile
- markdown
- nginx
- objectivec
- perl
- php
- plaintext
- properties
- python
- r
- ruby
- rust
- scala
- scss
- shell
- sql
- swift
- typescript
- x86asm
- xml
- yaml
Custom theme
Like the rest of the theme, the files used for syntax highlighting can be overridden with your own.
- highlight.js normally you shouldn't have to overwrite this file, unless you want to use a more recent version.
- highlight.css theme used by highlight.js for syntax highlighting.
If you want to use another theme for highlight.js
download it from their
website, or make it yourself, rename it to highlight.css
and put it in
the theme
folder of your book.
Now your theme will be used instead of the default theme.
Hiding code lines
There is a feature in mdBook that lets you hide code lines by prepending them
with a #
.
# fn main() {
let x = 5;
let y = 6;
println!("{}", x + y);
# }
Will render as
fn main() { let x = 5; let y = 7; println!("{}", x + y); }
At the moment, this only works for code examples that are annotated with
rust
. Because it would collide with semantics of some programming languages.
In the future, we want to make this configurable through the book.toml
so that
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 issue 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.
Editor
In addition to providing runnable code playgrounds, mdBook optionally allows them to be editable. In order to enable editable code blocks, the following needs to be added to the book.toml:
[output.html.playground]
editable = true
To make a specific block available for editing, the attribute editable
needs
to be added to it:
```rust,editable
fn main() {
let number = 5;
print!("{}", number);
}
```
The above will result in this editable playground:
fn main() { let number = 5; print!("{}", number); }
Note the new Undo Changes
button in the editable playgrounds.
Customizing the Editor
By default, the editor is the Ace editor, but, if desired, the functionality may be overridden by providing a different folder:
[output.html.playground]
editable = true
editor = "/path/to/editor"
Note that for the editor changes to function correctly, the book.js
inside of
the theme
folder will need to be overridden as it has some couplings with the
default Ace editor.
MathJax Support
mdBook has optional support for math equations through MathJax.
To enable MathJax, you need to add the mathjax-support
key to your book.toml
under the output.html
section.
[output.html]
mathjax-support = true
Note: The usual delimiters MathJax uses are not yet supported. You can't currently use
$$ ... $$
as delimiters and the\[ ... \]
delimiters need an extra backslash to work. Hopefully this limitation will be lifted soon.
Note: When you use double backslashes in MathJax blocks (for example in commands such as
\begin{cases} \frac 1 2 \\ \frac 3 4 \end{cases}
) you need to add two extra backslashes (e.g.,\begin{cases} \frac 1 2 \\\\ \frac 3 4 \end{cases}
).
Inline equations
Inline equations are delimited by \\(
and \\)
. So for example, to render the
following inline equation \( \int x dx = \frac{x^2}{2} + C \) you would write
the following:
\\( \int x dx = \frac{x^2}{2} + C \\)
Block equations
Block equations are delimited by \\[
and \\]
. To render the following
equation
\[ \mu = \frac{1}{N} \sum_{i=0} x_i \]
you would write:
\\[ \mu = \frac{1}{N} \sum_{i=0} x_i \\]
mdBook-specific features
Hiding code lines
There is a feature in mdBook that lets you hide code lines by prepending them
with a #
like you would with Rustdoc.
This currently only works with Rust language code blocks.
# fn main() {
let x = 5;
let y = 6;
println!("{}", x + y);
# }
Will render as
fn main() { let x = 5; let y = 6; println!("{}", x + y); }
The code block has an eyeball icon () which will toggle the visibility of the hidden lines.
Rust Playground
Rust language code blocks will automatically get a play button () which will execute the code and display the output just below the code block. This works by sending the code to the Rust Playground.
#![allow(unused)] fn main() { println!("Hello, World!"); }
If there is no main
function, then the code is automatically wrapped inside one.
If you wish to disable the play button for a code block, you can include the noplayground
option on the code block like this:
```rust,noplayground
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("failed to read line");
println!("Hello {}!", name);
```
Or, if you wish to disable the play button for all code blocks in your book, you can write the config to the book.toml
like this.
[output.html.playground]
runnable = false
Rust code block attributes
Additional attributes can be included in Rust code blocks with comma, space, or tab-separated terms just after the language term. For example:
```rust,ignore
# This example won't be tested.
panic!("oops!");
```
These are particularly important when using mdbook test
to test Rust examples.
These use the same attributes as rustdoc attributes, with a few additions:
editable
— Enables the editor.noplayground
— Removes the play button, but will still be tested.mdbook-runnable
— Forces the play button to be displayed. This is intended to be combined with theignore
attribute for examples that should not be tested, but you want to allow the reader to run.ignore
— Will not be tested and no play button is shown, but it is still highlighted as Rust syntax.should_panic
— When executed, it should produce a panic.no_run
— The code is compiled when tested, but it is not run. The play button is also not shown.compile_fail
— The code should fail to compile.edition2015
,edition2018
,edition2021
— Forces the use of a specific Rust edition. Seerust.edition
to set this globally.
Including files
With the following syntax, you can include files into your book:
{{#include file.rs}}
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.
```
{{#include file.rs}}
```
Including portions of a file
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:
{{#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.
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
any kind of commented line.
Consider the following file to include:
/* ANCHOR: all */
// ANCHOR: component
struct Paddle {
hello: f32,
}
// ANCHOR_END: component
////////// ANCHOR: system
impl System for MySystem { ... }
////////// ANCHOR_END: system
/* ANCHOR_END: all */
Then in the book, all you have to do is:
Here is a component:
```rust,no_run,noplayground
{{#include file.rs:component}}
```
Here is a system:
```rust,no_run,noplayground
{{#include file.rs:system}}
```
This is the full file.
```rust,no_run,noplayground
{{#include file.rs:all}}
```
Lines containing anchor patterns inside the included anchor are ignored.
Including a file but initially hiding all except specified lines
The rustdoc_include
helper is for including code from external Rust files that contain complete
examples, but only initially showing particular lines specified with line numbers or anchors in the
same way as with include
.
The lines not in the line number range or between the anchors will still be included, but they will
be prefaced with #
. This way, a reader can expand the snippet to see the complete example, and
Rustdoc will use the complete example when you run mdbook test
.
For example, consider a file named file.rs
that contains this Rust program:
fn main() { let x = add_one(2); assert_eq!(x, 3); } fn add_one(num: i32) -> i32 { num + 1 }
We can include a snippet that initially shows only line 2 by using this syntax:
To call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:
```rust
{{#rustdoc_include file.rs:2}}
```
This would have the same effect as if we had manually inserted the code and hidden all but line 2
using #
:
To call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:
```rust
# fn main() {
let x = add_one(2);
# assert_eq!(x, 3);
# }
#
# fn add_one(num: i32) -> i32 {
# num + 1
# }
```
That is, it looks like this (click the "expand" icon to see the rest of the file):
fn main() { let x = add_one(2); assert_eq!(x, 3); } fn add_one(num: i32) -> i32 { num + 1 }
Inserting runnable Rust files
With the following syntax, you can insert runnable Rust files into your book:
{{#playground 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 sent to the Rust Playground to be compiled and run. The result is sent back and displayed directly underneath the code.
Here is what a rendered code snippet looks like:
fn main() { println!("Hello World!"); // You can even hide lines! :D println!("I am hidden! Expand the code snippet to see me"); }
Any additional values passed after the filename will be included as attributes of the code block.
For example {{#playground example.rs editable}}
will create the code block like the following:
```rust,editable
# Contents of example.rs here.
```
And the editable
attribute will enable the editor as described at Rust code block attributes.
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.
{{#title My Title}}
Markdown
mdBook's parser adheres to the CommonMark specification with some extensions described below. You can take a quick tutorial, or try out CommonMark in real time. A complete Markdown overview is out of scope for this documentation, but below is a high level overview of some of the basics. For a more in-depth experience, check out the Markdown Guide.
Text and Paragraphs
Text is rendered relatively predictably:
Here is a line of text.
This is a new line.
Will look like you might expect:
Here is a line of text.
This is a new line.
Headings
Headings use the #
marker and should be on a line by themselves. More #
mean smaller headings:
### A heading
Some text.
#### A smaller heading
More text.
A heading
Some text.
A smaller heading
More text.
Lists
Lists can be unordered or ordered. Ordered lists will order automatically:
* milk
* eggs
* butter
1. carrots
1. celery
1. radishes
- milk
- eggs
- butter
- carrots
- celery
- radishes
Links
Linking to a URL or local file is easy:
Use [mdBook](https://github.com/rust-lang/mdBook).
Read about [mdBook](mdBook.md).
A bare url: <https://www.rust-lang.org>.
Use mdBook.
Read about mdBook.
A bare url: https://www.rust-lang.org.
Relative links that end with .md
will be converted to the .html
extension.
It is recommended to use .md
links when possible.
This is useful when viewing the Markdown file outside of mdBook, for example on GitHub or GitLab which render Markdown automatically.
Links to README.md
will be converted to index.html
.
This is done since some services like GitHub render README files automatically, but web servers typically expect the root file to be called index.html
.
You can link to individual headings with #
fragments.
For example, mdbook.md#text-and-paragraphs
would link to the Text and Paragraphs section above.
The ID is created by transforming the heading such as converting to lowercase and replacing spaces with dashes.
You can click on any heading and look at the URL in your browser to see what the fragment looks like.
Images
Including images is simply a matter of including a link to them, much like in the Links section above. The following markdown
includes the Rust logo SVG image found in the images
directory at the same level as this file:

Produces the following HTML when built with mdBook:
<p><img src="images/rust-logo-blk.svg" alt="The Rust Logo" /></p>
Which, of course displays the image like so:
Extensions
mdBook has several extensions beyond the standard CommonMark specification.
Strikethrough
Text may be rendered with a horizontal line through the center by wrapping the text with two tilde characters on each side:
An example of ~~strikethrough text~~.
This example will render as:
An example of
strikethrough text.
This follows the GitHub Strikethrough extension.
Footnotes
A footnote generates a small numbered link in the text which when clicked takes the reader to the footnote text at the bottom of the item. The footnote label is written similarly to a link reference with a caret at the front. The footnote text is written like a link reference definition, with the text following the label. Example:
This is an example of a footnote[^note].
[^note]: This text is the contents of the footnote, which will be rendered
towards the bottom.
This example will render as:
This is an example of a footnote1.
1This text is the contents of the footnote, which will be rendered towards the bottom.
The footnotes are automatically numbered based on the order the footnotes are written.
Tables
Tables can be written using pipes and dashes to draw the rows and columns of the table. These will be translated to HTML table matching the shape. Example:
| Header1 | Header2 |
|---------|---------|
| abc | def |
This example will render similarly to this:
Header1 | Header2 |
---|---|
abc | def |
See the specification for the GitHub Tables extension for more details on the exact syntax supported.
Task lists
Task lists can be used as a checklist of items that have been completed. Example:
- [x] Complete task
- [ ] Incomplete task
This will render as:
- Complete task
- Incomplete task
See the specification for the task list extension for more details.
Smart punctuation
Some ASCII punctuation sequences will be automatically turned into fancy Unicode characters:
ASCII sequence | Unicode |
---|---|
-- | – |
--- | — |
... | … |
" | “ or ”, depending on context |
' | ‘ or ’, depending on context |
So, no need to manually enter those Unicode characters!
This feature is disabled by default.
To enable it, see the output.html.curly-quotes
config option.
Running mdbook
in Continuous Integration
There are a variety of services such as GitHub Actions or GitLab CI/CD which can be used to test and deploy your book automatically.
The following provides some general guidelines on how to configure your service to run mdBook. Specific recipes can be found at the Automated Deployment wiki page.
Installing mdBook
There are several different strategies for installing mdBook. The particular method depends on your needs and preferences.
Pre-compiled binaries
Perhaps the easiest method is to use the pre-compiled binaries found on the GitHub Releases page.
A simple approach would be to use the popular curl
CLI tool to download the executable:
mkdir bin
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.18/mdbook-v0.4.18-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
bin/mdbook build
Some considerations for this approach:
- This is relatively fast, and does not necessarily require dealing with caching.
- This does not require installing Rust.
- Specifying a specific URL means you have to manually update your script to get a new version. This may be a benefit if you want to lock to a specific version. However, some users prefer to automatically get a newer version when they are published.
- You are reliant on the GitHub CDN being available.
Building from source
Building from source will require having Rust installed. Some services have Rust pre-installed, but if your service does not, you will need to add a step to install it.
After Rust is installed, cargo install
can be used to build and install mdBook.
We recommend using a SemVer version specifier so that you get the latest non-breaking version of mdBook.
For example:
cargo install mdbook --no-default-features --features search --vers "^0.4" --locked
This includes several recommended options:
--no-default-features
— Disables features like the HTTP server used bymdbook serve
that is likely not needed on CI. This will speed up the build time significantly.--features search
— Disabling default features means you should then manually enable features that you want, such as the built-in search capability.--vers "^0.4"
— This will install the most recent version of the0.4
series. However, versions after like0.5.0
won't be installed, as they may break your build. Cargo will automatically upgrade mdBook if you have an older version already installed.--locked
— This will use the dependencies that were used when mdBook was released. Without--locked
, it will use the latest version of all dependencies, which may include some fixes since the last release, but may also (rarely) cause build problems.
You will likely want to investigate caching options, as building mdBook can be somewhat slow.
Running tests
You may want to run tests using mdbook test
every time you push a change or create a pull request.
This can be used to validate Rust code examples in the book.
This will require having Rust installed. Some services have Rust pre-installed, but if your service does not, you will need to add a step to install it.
Other than making sure the appropriate version of Rust is installed, there's not much more than just running mdbook test
from the book directory.
You may also want to consider running other kinds of tests, like mdbook-linkcheck which will check for broken links. Or if you have your own style checks, spell checker, or any other tests it might be good to run them in CI.
Deploying
You may want to automatically deploy your book. Some may want to do this with every time a change is pushed, and others may want to only deploy when a specific release is tagged.
You'll also need to understand the specifics on how to push a change to your web service. For example, GitHub Pages just requires committing the output onto a specific git branch. Other services may require using something like SSH to connect to a remote server.
The basic outline is that you need to run mdbook build
to generate the output, and then transfer the files (which are in the book
directory) to the correct location.
You may then want to consider if you need to invalidate any caches on your web service.
See the Automated Deployment wiki page for examples of various different services.
404 handling
mdBook automatically generates a 404 page to be used for broken links.
The default output is a file named 404.html
at the root of the book.
Some services like GitHub Pages will automatically use this page for broken links.
For other services, you may want to consider configuring the web server to use this page as it will provide the reader navigation to get back to the book.
If your book is not deployed at the root of the domain, then you should set the output.html.site-url
setting so that the 404 page works correctly.
It needs to know where the book is deployed in order to load the static files (like CSS) correctly.
For example, this guide is deployed at https://rust-lang.github.io/mdBook/, and the site-url
setting is configured like this:
# book.toml
[output.html]
site-url = "/mdBook/"
You can customize the look of the 404 page by creating a file named src/404.md
in your book.
If you want to use a different filename, you can set output.html.input-404
to a different filename.
For Developers
While mdbook
is mainly used as a command line tool, you can also import the
underlying library directly and use that to manage a book. It also has a fairly
flexible plugin mechanism, allowing you to create your own custom tooling and
consumers (often referred to as backends) if you need to do some analysis of
the book or render it in a different format.
The For Developers chapters are here to show you the more advanced usage of
mdbook
.
The two main ways a developer can hook into the book's build process is via,
The Build Process
The process of rendering a book project goes through several steps.
- Load the book
- Parse the
book.toml
, falling back to the defaultConfig
if it doesn't exist - Load the book chapters into memory
- Discover which preprocessors/backends should be used
- Parse the
- For each backend:
- Run all the preprocessors.
- Call the backend to render the processed result.
Using mdbook
as a Library
The mdbook
binary is just a wrapper around the mdbook
crate, exposing its
functionality as a command-line program. As such it is quite easy to create your
own programs which use mdbook
internally, adding your own functionality (e.g.
a custom preprocessor) or tweaking the build process.
The easiest way to find out how to use the mdbook
crate is by looking at the
API Docs. The top level documentation explains how one would use the
MDBook
type to load and build a book, while the config module gives a good
explanation on the configuration system.
Preprocessors
A preprocessor is simply a bit of code which gets run immediately after the book is loaded and before it gets rendered, allowing you to update and mutate the book. Possible use cases are:
- Creating custom helpers like
{{#include /path/to/file.md}}
- Substituting in latex-style expressions (
$$ \frac{1}{3} $$
) with their mathjax equivalents
See Configuring Preprocessors for more information about using preprocessors.
Hooking Into MDBook
MDBook uses a fairly simple mechanism for discovering third party plugins.
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.
Once the preprocessor has been defined and the build process starts, mdBook executes the command defined in the preprocessor.foo.command
key twice.
The first time it runs the preprocessor to determine if it supports the given renderer.
mdBook passes two arguments to the process: the first argument is the string supports
and the second argument is the renderer name.
The preprocessor should exit with a status code 0 if it supports the given renderer, or return a non-zero exit code if it does not.
If the preprocessor supports the renderer, then mdbook runs it a second time, passing JSON data into stdin.
The JSON consists of an array of [context, book]
where context
is the serialized object PreprocessorContext
and book
is a Book
object containing the content of the book.
The preprocessor should return the JSON format of the Book
object to stdout, with any modifications it wishes to perform.
The easiest way to get started is by creating your own implementation of the
Preprocessor
trait (e.g. in lib.rs
) and then creating a shell binary which
translates inputs to the correct Preprocessor
method. For convenience, there
is an example no-op preprocessor in the examples/
directory which can easily
be adapted for other preprocessors.
Example no-op preprocessor
#![allow(unused)] fn main() { // nop-preprocessors.rs {{#include ../../../examples/nop-preprocessor.rs}} }
Hints For Implementing A Preprocessor
By pulling in mdbook
as a library, preprocessors can have access to the
existing infrastructure for dealing with books.
For example, a custom preprocessor could use the
CmdPreprocessor::parse_input()
function to deserialize the JSON written to
stdin
. Then each chapter of the Book
can be mutated in-place via
Book::for_each_mut()
, and then written to stdout
with the serde_json
crate.
Chapters can be accessed either directly (by recursively iterating over
chapters) or via the Book::for_each_mut()
convenience method.
The chapter.content
is just a string which happens to be markdown. While it's
entirely possible to use regular expressions or do a manual find & replace,
you'll probably want to process the input into something more computer-friendly.
The pulldown-cmark
crate implements a production-quality event-based
Markdown parser, with the pulldown-cmark-to-cmark
crate allowing you to
translate events back into markdown text.
The following code block shows how to remove all emphasis from markdown, without accidentally breaking the document.
#![allow(unused)] fn main() { fn remove_emphasis( num_removed_items: &mut usize, chapter: &mut Chapter, ) -> Result<String> { let mut buf = String::with_capacity(chapter.content.len()); let events = Parser::new(&chapter.content).filter(|e| { let should_keep = match *e { Event::Start(Tag::Emphasis) | Event::Start(Tag::Strong) | Event::End(Tag::Emphasis) | Event::End(Tag::Strong) => false, _ => true, }; if !should_keep { *num_removed_items += 1; } should_keep }); cmark(events, &mut buf, None).map(|_| buf).map_err(|err| { Error::from(format!("Markdown serialization failed: {}", err)) }) } }
For everything else, have a look at the complete example.
Implementing a preprocessor with a different language
The fact that mdBook utilizes stdin and stdout to communicate with the preprocessors makes it easy to implement them in a language other than Rust.
The following code shows how to implement a simple preprocessor in Python, which will modify the content of the first chapter.
The example below follows the configuration shown above with preprocessor.foo.command
actually pointing to a Python script.
import json
import sys
if __name__ == '__main__':
if len(sys.argv) > 1: # we check if we received any argument
if sys.argv[1] == "supports":
# then we are good to return an exit status code of 0, since the other argument will just be the renderer's name
sys.exit(0)
# load both the context and the book representations from stdin
context, book = json.load(sys.stdin)
# and now, we can just modify the content of the first chapter
book['sections'][0]['Chapter']['content'] = '# Hello'
# we are done with the book's modification, we can just print it to stdout,
print(json.dumps(book))
Alternative Backends
A "backend" is simply a program which mdbook
will invoke during the book
rendering process. This program is passed a JSON representation of the book and
configuration information via stdin
. Once the backend receives this
information it is free to do whatever it wants.
See Configuring Renderers for more information about using backends.
The community has developed several backends. See the Third Party Plugins wiki page for a list of available backends.
Setting Up
This page will step you through creating your own alternative backend in the form of a simple word counting program. Although it will be written in Rust, there's no reason why it couldn't be accomplished using something like Python or Ruby.
First you'll want to create a new binary program and add mdbook
as a
dependency.
$ cargo new --bin mdbook-wordcount
$ cd mdbook-wordcount
$ cargo add mdbook
When our mdbook-wordcount
plugin is invoked, mdbook
will send it a JSON
version of RenderContext
via our plugin's stdin
. For convenience, there's
a RenderContext::from_json()
constructor which will load a RenderContext
.
This is all the boilerplate necessary for our backend to load the book.
// src/main.rs extern crate mdbook; use std::io; use mdbook::renderer::RenderContext; fn main() { let mut stdin = io::stdin(); let ctx = RenderContext::from_json(&mut stdin).unwrap(); }
Note: The
RenderContext
contains aversion
field. This lets backends figure out whether they are compatible with the version ofmdbook
it's being called by. Thisversion
comes directly from the corresponding field inmdbook
'sCargo.toml
.
It is recommended that backends use the semver
crate to inspect this field
and emit a warning if there may be a compatibility issue.
Inspecting the Book
Now our backend has a copy of the book, lets count how many words are in each chapter!
Because the RenderContext
contains a Book
field (book
), and a Book
has
the Book::iter()
method for iterating over all items in a Book
, this step
turns out to be just as easy as the first.
fn main() { let mut stdin = io::stdin(); let ctx = RenderContext::from_json(&mut stdin).unwrap(); for item in ctx.book.iter() { if let BookItem::Chapter(ref ch) = *item { let num_words = count_words(ch); println!("{}: {}", ch.name, num_words); } } } fn count_words(ch: &Chapter) -> usize { ch.content.split_whitespace().count() }
Enabling the Backend
Now we've got the basics running, we want to actually use it. First, install the program.
$ cargo install --path .
Then cd
to the particular book you'd like to count the words of and update its
book.toml
file.
[book]
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
authors = ["Mathieu David", "Michael-F-Bryan"]
+ [output.html]
+ [output.wordcount]
When it loads a book into memory, mdbook
will inspect your book.toml
file to
try and figure out which backends to use by looking for all output.*
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 table just stays empty.
Now you just need to build your book like normal, and everything should Just Work.
$ mdbook build
...
2018-01-16 07:31:15 [INFO] (mdbook::renderer): Invoking the "mdbook-wordcount" renderer
mdBook: 126
Command Line Tool: 224
init: 283
build: 145
watch: 146
serve: 292
test: 139
Format: 30
SUMMARY.md: 259
Configuration: 784
Theme: 304
index.hbs: 447
Syntax highlighting: 314
MathJax Support: 153
Rust code specific features: 148
For Developers: 788
Alternative Backends: 710
Contributors: 85
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
arguments or be an interpreted script), you can use the command
field.
[book]
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
authors = ["Mathieu David", "Michael-F-Bryan"]
[output.html]
[output.wordcount]
+ command = "python /path/to/wordcount.py"
Configuration
Now imagine you don't want to count the number of words on a particular chapter
(it might be generated text/code, etc). The canonical way to do this is via the
usual book.toml
configuration file by adding items to your [output.foo]
table.
The Config
can be treated roughly as a nested hashmap which lets you call
methods like get()
to access the config's contents, with a
get_deserialized()
convenience method for retrieving a value and automatically
deserializing to some arbitrary type T
.
To implement this, we'll create our own serializable WordcountConfig
struct
which will encapsulate all configuration for this backend.
First add serde
and serde_derive
to your Cargo.toml
,
$ cargo add serde serde_derive
And then you can create the config struct,
#![allow(unused)] fn main() { extern crate serde; #[macro_use] extern crate serde_derive; ... #[derive(Debug, Default, Serialize, Deserialize)] #[serde(default, rename_all = "kebab-case")] pub struct WordcountConfig { pub ignores: Vec<String>, } }
Now we just need to deserialize the WordcountConfig
from our RenderContext
and then add a check to make sure we skip ignored chapters.
fn main() {
let mut stdin = io::stdin();
let ctx = RenderContext::from_json(&mut stdin).unwrap();
+ let cfg: WordcountConfig = ctx.config
+ .get_deserialized("output.wordcount")
+ .unwrap_or_default();
for item in ctx.book.iter() {
if let BookItem::Chapter(ref ch) = *item {
+ if cfg.ignores.contains(&ch.name) {
+ continue;
+ }
+
let num_words = count_words(ch);
println!("{}: {}", ch.name, num_words);
}
}
}
Output and Signalling Failure
While it's nice to print word counts to the terminal when a book is built, it
might also be a good idea to output them to a file somewhere. mdbook
tells a
backend where it should place any generated output via the destination
field
in RenderContext
.
+ use std::fs::{self, File};
+ use std::io::{self, Write};
- use std::io;
use mdbook::renderer::RenderContext;
use mdbook::book::{BookItem, Chapter};
fn main() {
...
+ let _ = fs::create_dir_all(&ctx.destination);
+ let mut f = File::create(ctx.destination.join("wordcounts.txt")).unwrap();
+
for item in ctx.book.iter() {
if let BookItem::Chapter(ref ch) = *item {
...
let num_words = count_words(ch);
println!("{}: {}", ch.name, num_words);
+ writeln!(f, "{}: {}", ch.name, num_words).unwrap();
}
}
}
Note: There is no guarantee that the destination directory exists or is empty (
mdbook
may leave the previous contents to let backends do caching), so it's always a good idea to create it withfs::create_dir_all()
.If the destination directory already exists, don't assume it will be empty. To allow backends to cache the results from previous runs,
mdbook
may leave old content in the directory.
There's always the possibility that an error will occur while processing a book
(just look at all the unwrap()
's we've written already), so mdbook
will
interpret a non-zero exit code as a rendering failure.
For example, if we wanted to make sure all chapters have an even number of words, erroring out if an odd number is encountered, then you may do something like this:
+ use std::process;
...
fn main() {
...
for item in ctx.book.iter() {
if let BookItem::Chapter(ref ch) = *item {
...
let num_words = count_words(ch);
println!("{}: {}", ch.name, num_words);
writeln!(f, "{}: {}", ch.name, num_words).unwrap();
+ if cfg.deny_odds && num_words % 2 == 1 {
+ eprintln!("{} has an odd number of words!", ch.name);
+ process::exit(1);
}
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct WordcountConfig {
pub ignores: Vec<String>,
+ pub deny_odds: bool,
}
Now, if we reinstall the backend and build a book,
$ cargo install --path . --force
$ mdbook build /path/to/book
...
2018-01-16 21:21:39 [INFO] (mdbook::renderer): Invoking the "wordcount" renderer
mdBook: 126
Command Line Tool: 224
init: 283
init has an odd number of words!
2018-01-16 21:21:39 [ERROR] (mdbook::renderer): Renderer exited with non-zero return code.
2018-01-16 21:21:39 [ERROR] (mdbook::utils): Error: Rendering failed
2018-01-16 21:21:39 [ERROR] (mdbook::utils): Caused By: The "mdbook-wordcount" renderer failed
As you've probably already noticed, output from the plugin's subprocess is immediately passed through to the user. It is encouraged for plugins to follow the "rule of silence" and only generate output when necessary (e.g. an error in generation or a warning).
All environment variables are passed through to the backend, allowing you to use
the usual RUST_LOG
to control logging verbosity.
Wrapping Up
Although contrived, hopefully this example was enough to show how you'd create
an alternative backend for mdbook
. If you feel it's missing something, don't
hesitate to create an issue in the issue tracker so we can improve the user
guide.
The existing backends mentioned towards the start of this chapter should serve as a good example of how it's done in real life, so feel free to skim through the source code or ask questions.
Contributors
Here is a list of the contributors who have helped improving mdBook. Big shout-out to them!
- mdinger
- Kevin (kbknapp)
- Steve Klabnik (steveklabnik)
- Adam Solove (asolove)
- Wayne Nilsen (waynenilsen)
- funnkill
- Fu Gangqiang (FuGangqiang)
- Michael-F-Bryan
- Chris Spiegel (cspiegel)
- projektir
- Phaiax
- Matt Ickstadt (mattico)
- Weihang Lo (weihanglo)
- Avision Ho (avisionh)
- Vivek Akupatni (apatniv)
- Eric Huss (ehuss)
- Josh Rotenberg (joshrotenberg)
If you feel you're missing from this list, feel free to add yourself in a PR.