Skip to content

Commit 881f1b1

Browse files
committed
Support dollar math syntax (fixes #1402, #662, #400)
Letting MathJax scrape the output HTML for math delimiters makes it awkward to write complex formulas because characters with a special meaning in Markdown must be escaped. This commit implements a new `output.html.math` option that uses the support for parsing `$...$` and `$$...$$` in Markdown introduced in pulldown-cmark 0.11.0. The old option `output.html.mathjax-support` is left for backwards compatibility. pulldown-cmark renders formulas into `<span>` tags with special classes, so we have to configure MathJax to look for these instead of textual delimiters. The code for this was helpfully provided by David Cervone on <https://groups.google.com/g/mathjax-users/c/6cMuCH2dgmQ>. The latest version of MathJax 4 is used rather than MathJax 2.7.1 like the mathjax-support option, because the way to do this configuration has changed between MathJax 2 and MathJax 3, I'm not sure how to do it on MathJax 2, and it is an old version anyway. The Cloudflare CDN doesn't seem to work for MathJax 4, so it uses the jsdelivr.net one recommended by the MathJax documentation. In the future, it would be good to use a bundled version of MathJax by default and/or make the CDN URL configurable. Fixes #1402, fixes #662, fixes #400
1 parent d63aeb6 commit 881f1b1

File tree

14 files changed

+95
-35
lines changed

14 files changed

+95
-35
lines changed

crates/mdbook-core/src/config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,9 @@ pub struct HtmlConfig {
460460
pub definition_lists: bool,
461461
/// Support for admonitions.
462462
pub admonitions: bool,
463-
/// Should mathjax be enabled?
463+
/// Parse and render `$...$` and `$$...$$` as math formulas.
464+
pub math: bool,
465+
/// Legacy math support.
464466
pub mathjax_support: bool,
465467
/// Additional CSS stylesheets to include in the rendered page's `<head>`.
466468
pub additional_css: Vec<PathBuf>,
@@ -529,6 +531,7 @@ impl Default for HtmlConfig {
529531
smart_punctuation: true,
530532
definition_lists: true,
531533
admonitions: true,
534+
math: false,
532535
mathjax_support: false,
533536
additional_css: Vec::new(),
534537
additional_js: Vec::new(),

crates/mdbook-html/front-end/templates/index.hbs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,36 @@
4545
<link rel="stylesheet" href="{{ resource this }}">
4646
{{/each}}
4747

48-
{{#if mathjax_support}}
49-
<!-- MathJax -->
48+
{{#if math}}
49+
<!-- Load MathJax -->
50+
{{! MathJax normally looks for math tags in the page content. In our case,
51+
the math tags are parsed in the Markdown input by pulldown-cmark and
52+
converted to <span> tags with special CSS classes. This bit of code
53+
tells MathJax to render these and only these. Adapted from
54+
<https://docs.mathjax.org/en/latest/upgrading/v2.html#math-script-example> }}
55+
<script>
56+
MathJax = {
57+
options: {
58+
renderActions: {
59+
find: [10, function (doc) {
60+
for (const node of document.querySelectorAll('span.math-inline, span.math-display')) {
61+
const display = node.classList.contains('math-display');
62+
const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
63+
const text = document.createTextNode('');
64+
node.parentNode.replaceChild(text, node);
65+
math.start = {node: text, delim: '', n: 0};
66+
math.end = {node: text, delim: '', n: 0};
67+
doc.math.push(math);
68+
}
69+
}, '']
70+
}
71+
}
72+
};
73+
</script>
74+
<script defer src="https://cdn.jsdelivr.net/npm/mathjax@4/tex-mml-chtml.js"></script>
75+
{{else if mathjax_support}}
76+
<!-- Load MathJax -->
77+
{{! Legacy MathJax support that just tells MathJax to look for math tags in the page content }}
5078
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
5179
{{/if}}
5280

crates/mdbook-html/src/html/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl<'a> HtmlRenderOptions<'a> {
5353
markdown_options.smart_punctuation = config.smart_punctuation;
5454
markdown_options.definition_lists = config.definition_lists;
5555
markdown_options.admonitions = config.admonitions;
56+
markdown_options.math = config.math;
5657
HtmlRenderOptions {
5758
markdown_options,
5859
path,

crates/mdbook-html/src/html_handlebars/hbs_renderer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ fn make_data(
506506
json!(preferred_dark_theme),
507507
);
508508

509+
if html_config.math {
510+
data.insert("math".to_owned(), json!(true));
511+
}
512+
509513
if html_config.mathjax_support {
510514
data.insert("mathjax_support".to_owned(), json!(true));
511515
}

crates/mdbook-markdown/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ pub struct MarkdownOptions {
2828
///
2929
/// This is `true` by default.
3030
pub admonitions: bool,
31+
/// Enables `$...$` and `$$...$$` syntax for inline and display math,
32+
/// respectively.
33+
///
34+
/// This is `false` by default.
35+
pub math: bool,
3136
}
3237

3338
impl Default for MarkdownOptions {
@@ -36,6 +41,7 @@ impl Default for MarkdownOptions {
3641
smart_punctuation: true,
3742
definition_lists: true,
3843
admonitions: true,
44+
math: false,
3945
}
4046
}
4147
}
@@ -57,5 +63,8 @@ pub fn new_cmark_parser<'text>(text: &'text str, options: &MarkdownOptions) -> P
5763
if options.admonitions {
5864
opts.insert(Options::ENABLE_GFM);
5965
}
66+
if options.math {
67+
opts.insert(Options::ENABLE_MATH);
68+
}
6069
Parser::new_ext(text, opts)
6170
}

guide/book.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ edition = "2018"
99

1010
[output.html]
1111
smart-punctuation = true
12-
mathjax-support = true
12+
math = true
1313
site-url = "/mdBook/"
1414
git-repository-url = "https://github.com/rust-lang/mdBook/tree/master/guide"
1515
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"

guide/src/format/configuration/renderers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ preferred-dark-theme = "navy"
100100
smart-punctuation = true
101101
definition-lists = true
102102
admonitions = true
103-
mathjax-support = false
103+
math = false
104104
additional-css = ["custom.css", "custom2.css"]
105105
additional-js = ["custom.js"]
106106
no-section-label = false
@@ -129,7 +129,7 @@ The following configuration options are available:
129129
Defaults to `true`.
130130
- **definition-lists:** Enables [definition lists](../markdown.md#definition-lists). Defaults to `true`.
131131
- **admonitions:** Enables [admonitions](../markdown.md#admonitions). Defaults to `true`.
132-
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
132+
- **math:** Enables [support for math formulas](../mathjax.md). Defaults to
133133
`false`.
134134
- **additional-css:** If you need to slightly change the appearance of your book
135135
without overwriting the whole style, you can specify a set of stylesheets that

guide/src/format/mathjax.md

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,33 @@
11
# MathJax support
22

3-
mdBook has optional support for math equations through
4-
[MathJax](https://www.mathjax.org/).
5-
6-
To enable MathJax, you need to add the `mathjax-support` key to your `book.toml`
7-
under the `output.html` section.
3+
mdBook has optional support for math formulas, which are rendered through
4+
[MathJax](https://www.mathjax.org/) (more precisely, the latest release of
5+
MathJax version 4). To enable it, set `output.html.math` to `true` in your
6+
`book.toml`:
87

98
```toml
109
[output.html]
11-
mathjax-support = true
10+
math = true
1211
```
1312

14-
>**Note:** The usual delimiters MathJax uses are not yet supported. You can't
15-
currently use `$$ ... $$` as delimiters and the `\[ ... \]` delimiters need an
16-
extra backslash to work. Hopefully this limitation will be lifted soon.
13+
Inline equations are delimited by `$...$` and block equations are delimited
14+
by `$$...$$`. For example, to obtain
1715

18-
>**Note:** When you use double backslashes in MathJax blocks (for example in
19-
> commands such as `\begin{cases} \frac 1 2 \\ \frac 3 4 \end{cases}`) you need
20-
> to add _two extra_ backslashes (e.g., `\begin{cases} \frac 1 2 \\\\ \frac 3 4
21-
> \end{cases}`).
16+
> If $n \geq 3$ then there are no integers $a, b, c \geq 1$ satisfying $$a^n + b^n = c^n$$
2217
18+
you would write the following:
2319

24-
### Inline equations
25-
Inline equations are delimited by `\\(` and `\\)`. So for example, to render the
26-
following inline equation \\( \int x dx = \frac{x^2}{2} + C \\) you would write
27-
the following:
2820
```
29-
\\( \int x dx = \frac{x^2}{2} + C \\)
21+
If $n \geq 3$ then there are no integers $a, b, c \geq 1$ satisfying $$a^n + b^n = c^n$$
3022
```
3123

32-
### Block equations
33-
Block equations are delimited by `\\[` and `\\]`. To render the following
34-
equation
35-
36-
\\[ \mu = \frac{1}{N} \sum_{i=0} x_i \\]
37-
24+
## Legacy MathJax support
3825

39-
you would write:
40-
41-
```bash
42-
\\[ \mu = \frac{1}{N} \sum_{i=0} x_i \\]
43-
```
26+
The legacy option `output.html.mathjax-support` enables equations with a
27+
different syntax: `\\( ... \\)` for inline equations and `\\[ ... \\]` for
28+
block equations. Because it does not parse formulas in the Markdown input
29+
but instead lets MathJax find the delimiters in the HTML output, it has
30+
the limitation that characters which have an effect in Markdown, such as
31+
underscores, need to be escaped in formulas. This option is kept for
32+
backwards compatibility; use `output.html.math` in new books. It uses
33+
MathJax 2.7.1.

tests/testsuite/markdown.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,19 @@ fn definition_lists() {
164164
);
165165
}
166166

167+
#[test]
168+
fn math() {
169+
BookTest::from_dir("markdown/math")
170+
.check_all_main_files()
171+
.run("build", |cmd| {
172+
cmd.env("MDBOOK_OUTPUT__HTML__MATH", "true");
173+
})
174+
.check_main_file(
175+
"book/math.html",
176+
file!["markdown/math/expected_enabled/math.html"],
177+
);
178+
}
179+
167180
#[test]
168181
fn admonitions() {
169182
BookTest::from_dir("markdown/admonitions")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[book]
2+
title = "math"

0 commit comments

Comments
 (0)