@@ -11,68 +11,71 @@ the book. Possible use cases are:
11
11
mathjax equivalents
12
12
13
13
14
- ## Implementing a Preprocessor
14
+ ## Hooking Into MDBook
15
+
16
+ MDBook uses a fairly simple mechanism for discovering third party plugins.
17
+ A new table is added to ` book.toml ` (e.g. ` preprocessor.foo ` for the ` foo `
18
+ preprocessor) and then ` mdbook ` will try to invoke the ` mdbook-foo ` program as
19
+ part of the build process.
20
+
21
+ While preprocessors can be hard-coded to specify which backend it should be run
22
+ for (e.g. it doesn't make sense for MathJax to be used for non-HTML renderers)
23
+ with the ` preprocessor.foo.renderer ` key.
24
+
25
+ ``` toml
26
+ [book ]
27
+ title = " My Book"
28
+ authors = [" Michael-F-Bryan" ]
29
+
30
+ [preprocessor .foo ]
31
+ # The command can also be specified manually
32
+ command = " python3 /path/to/foo.py"
33
+ # Only run the `foo` preprocessor for the HTML and EPUB renderer
34
+ renderer = [" html" , " epub" ]
35
+ ```
15
36
16
- A preprocessor is represented by the ` Preprocessor ` trait.
37
+ In typical unix style, all inputs to the plugin will be written to ` stdin ` as
38
+ JSON and ` mdbook ` will read from ` stdout ` if it is expecting output.
17
39
18
- ``` rust
19
- pub trait Preprocessor {
20
- fn name (& self ) -> & str ;
21
- fn run (& self , ctx : & PreprocessorContext , book : Book ) -> Result <Book >;
22
- fn supports_renderer (& self , _renderer : & str ) -> bool {
23
- true
24
- }
25
- }
26
- ```
40
+ The easiest way to get started is by creating your own implementation of the
41
+ ` Preprocessor ` trait (e.g. in ` lib.rs ` ) and then creating a shell binary which
42
+ translates inputs to the correct ` Preprocessor ` method. For convenience, there
43
+ is [ an example no-op preprocessor] in the ` examples/ ` directory which can easily
44
+ be adapted for other preprocessors.
27
45
28
- Where the ` PreprocessorContext ` is defined as
46
+ <details >
47
+ <summary >Example no-op preprocessor</summary >
29
48
30
49
``` rust
31
- pub struct PreprocessorContext {
32
- pub root : PathBuf ,
33
- pub config : Config ,
34
- /// The `Renderer` this preprocessor is being used with.
35
- pub renderer : String ,
36
- }
37
- ```
38
-
39
- The ` renderer ` value allows you react accordingly, for example, PDF or HTML.
50
+ // nop-preprocessors.rs
40
51
41
- ## A complete Example
52
+ {{#include .. / .. / .. / examples / nop - preprocessor . rs}}
53
+ ```
54
+ </details >
42
55
43
- The magic happens within the ` run(...) ` method of the
44
- [ ` Preprocessor ` ] [ preprocessor-docs ] trait implementation.
56
+ ## Hints For Implementing A Preprocessor
45
57
46
- As direct access to the chapters is not possible, you will probably end up
47
- iterating them using ` for_each_mut(...) ` :
58
+ By pulling in ` mdbook ` as a library, preprocessors can have access to the
59
+ existing infrastructure for dealing with books.
48
60
49
- ``` rust
50
- book . for_each_mut (| item : & mut BookItem | {
51
- if let BookItem :: Chapter (ref mut chapter ) = * item {
52
- eprintln! (" {}: processing chapter '{}'" , self . name (), chapter . name);
53
- res = Some (
54
- match Deemphasize :: remove_emphasis (& mut num_removed_items , chapter ) {
55
- Ok (md ) => {
56
- chapter . content = md ;
57
- Ok (())
58
- }
59
- Err (err ) => Err (err ),
60
- },
61
- );
62
- }
63
- });
64
- ```
61
+ For example, a custom preprocessor could use the
62
+ [ ` CmdPreprocessor::parse_input() ` ] function to deserialize the JSON written to
63
+ ` stdin ` . Then each chapter of the ` Book ` can be mutated in-place via
64
+ [ ` Book::for_each_mut() ` ] , and then written to ` stdout ` with the ` serde_json `
65
+ crate.
65
66
66
- The ` chapter.content ` is just a markdown formatted string, and you will have to
67
- process it in some way. Even though it's entirely possible to implement some
68
- sort of manual find & replace operation, if that feels too unsafe you can use
69
- [ ` pulldown-cmark ` ] [ pc ] to parse the string into events and work on them instead.
67
+ Chapters can be accessed either directly (by recursively iterating over
68
+ chapters) or via the ` Book::for_each_mut() ` convenience method.
70
69
71
- Finally you can use [ ` pulldown-cmark-to-cmark ` ] [ pctc ] to transform these events
72
- back to a string.
70
+ The ` chapter.content ` is just a string which happens to be markdown. While it's
71
+ entirely possible to use regular expressions or do a manual find & replace,
72
+ you'll probably want to process the input into something more computer-friendly.
73
+ The [ ` pulldown-cmark ` ] [ pc ] crate implements a production-quality event-based
74
+ Markdown parser, with the [ ` pulldown-cmark-to-cmark ` ] [ pctc ] allowing you to
75
+ translate events back into markdown text.
73
76
74
- The following code block shows how to remove all emphasis from markdown, and do
75
- so safely .
77
+ The following code block shows how to remove all emphasis from markdown,
78
+ without accidentally breaking the document .
76
79
77
80
``` rust
78
81
fn remove_emphasis (
@@ -107,3 +110,6 @@ For everything else, have a look [at the complete example][example].
107
110
[ pc ] : https://crates.io/crates/pulldown-cmark
108
111
[ pctc ] : https://crates.io/crates/pulldown-cmark-to-cmark
109
112
[ example ] : https://github.com/rust-lang-nursery/mdBook/blob/master/examples/de-emphasize.rs
113
+ [ an example no-op preprocessor ] : https://github.com/rust-lang-nursery/mdBook/blob/master/examples/nop-preprocessor.rs
114
+ [ `CmdPreprocessor::parse_input()` ] : https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html#method.parse_input
115
+ [ `Book::for_each_mut()` ] : https://docs.rs/mdbook/latest/mdbook/book/struct.Book.html#method.for_each_mut
0 commit comments