Skip to content

Commit ba4c3ed

Browse files
committed
Add support for definition lists
This enables the definition lists support from pulldown-cmark. This includes a config option in case it causes problems with existing books. Closes #2770
1 parent 53d39a8 commit ba4c3ed

File tree

13 files changed

+269
-5
lines changed

13 files changed

+269
-5
lines changed

crates/mdbook-core/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ pub struct HtmlConfig {
434434
pub preferred_dark_theme: Option<String>,
435435
/// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash.
436436
pub smart_punctuation: bool,
437+
/// Support for definition lists.
438+
pub definition_lists: bool,
437439
/// Should mathjax be enabled?
438440
pub mathjax_support: bool,
439441
/// Additional CSS stylesheets to include in the rendered page's `<head>`.
@@ -501,6 +503,7 @@ impl Default for HtmlConfig {
501503
default_theme: None,
502504
preferred_dark_theme: None,
503505
smart_punctuation: true,
506+
definition_lists: true,
504507
mathjax_support: false,
505508
additional_css: Vec::new(),
506509
additional_js: Vec::new(),

crates/mdbook-html/front-end/css/general.css

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ h2:target::before,
6161
h3:target::before,
6262
h4:target::before,
6363
h5:target::before,
64-
h6:target::before {
64+
h6:target::before,
65+
dt:target::before {
6566
display: inline-block;
6667
content: "»";
6768
margin-inline-start: -30px;
@@ -285,3 +286,41 @@ sup {
285286
fill: currentColor;
286287
margin-bottom: -0.1em;
287288
}
289+
290+
dt {
291+
font-weight: bold;
292+
margin-top: 0.5em;
293+
margin-bottom: 0.1em;
294+
}
295+
296+
/* This uses a CSS counter to add numbers to definitions, but only if there is
297+
more than one definition. */
298+
dl, dt {
299+
counter-reset: dd-counter;
300+
}
301+
302+
/* When there is more than one definition, increment the counter. The first
303+
selector selects the first definition, and the second one selects definitions
304+
2 and beyond.*/
305+
dd:has(+ dd), dd + dd {
306+
counter-increment: dd-counter;
307+
/* Use flex display to help with positioning the numbers when there is a p
308+
tag inside the definition. */
309+
display: flex;
310+
align-items: flex-start;
311+
}
312+
313+
/* Shows the counter for definitions. The first selector selects the first
314+
definition, and the second one selections definitions 2 and beyond.*/
315+
dd:has(+ dd)::before, dd + dd::before {
316+
content: counter(dd-counter) ". ";
317+
font-weight: 600;
318+
display: inline-block;
319+
margin-right: 0.5em;
320+
}
321+
322+
dd > p {
323+
/* For loose definitions that have a p tag inside, don't add a bunch of
324+
space before the definition. */
325+
margin-top: 0;
326+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl<'a> HtmlRenderOptions<'a> {
5050
) -> HtmlRenderOptions<'a> {
5151
let mut markdown_options = MarkdownOptions::default();
5252
markdown_options.smart_punctuation = config.smart_punctuation;
53+
markdown_options.definition_lists = config.definition_lists;
5354
HtmlRenderOptions {
5455
markdown_options,
5556
path,

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -818,12 +818,14 @@ where
818818
}
819819

820820
/// This is used after parsing is complete to add a unique `id` attribute
821-
/// to all header elements, and to also add an `<a>` tag so that clicking
822-
/// the header will set the current URL to that header's fragment.
821+
/// to all header and dt elements, and to also add an `<a>` tag so that
822+
/// clicking the element will set the current URL to that element's
823+
/// fragment.
823824
fn add_header_links(&mut self) {
824825
let mut id_counter = HashSet::new();
825-
let headings =
826-
self.node_ids_for_tag(&|name| matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6"));
826+
let headings = self.node_ids_for_tag(&|name| {
827+
matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "dt")
828+
});
827829
for heading in headings {
828830
let node = self.tree.get(heading).unwrap();
829831
let el = node.value().as_element().unwrap();

crates/mdbook-markdown/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ pub struct MarkdownOptions {
2424
///
2525
/// This is `true` by default.
2626
pub smart_punctuation: bool,
27+
/// Enables definition lists.
28+
///
29+
/// This is `true` by default.
30+
pub definition_lists: bool,
2731
}
2832

2933
impl Default for MarkdownOptions {
3034
fn default() -> MarkdownOptions {
3135
MarkdownOptions {
3236
smart_punctuation: true,
37+
definition_lists: true,
3338
}
3439
}
3540
}
@@ -45,5 +50,8 @@ pub fn new_cmark_parser<'text>(text: &'text str, options: &MarkdownOptions) -> P
4550
if options.smart_punctuation {
4651
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
4752
}
53+
if options.definition_lists {
54+
opts.insert(Options::ENABLE_DEFINITION_LIST);
55+
}
4856
Parser::new_ext(text, opts)
4957
}

guide/src/format/configuration/renderers.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ theme = "my-theme"
9898
default-theme = "light"
9999
preferred-dark-theme = "navy"
100100
smart-punctuation = true
101+
definition-lists = true
101102
mathjax-support = false
102103
additional-css = ["custom.css", "custom2.css"]
103104
additional-js = ["custom.js"]
@@ -125,6 +126,7 @@ The following configuration options are available:
125126
- **smart-punctuation:** Converts quotes to curly quotes, `...` to ``, `--` to en-dash, and `---` to em-dash.
126127
See [Smart Punctuation](../markdown.md#smart-punctuation).
127128
Defaults to `true`.
129+
- **definition-lists:** Enables [definition lists](../markdown.md#definition-lists). Defaults to `true`.
128130
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
129131
`false`.
130132
- **additional-css:** If you need to slightly change the appearance of your book

guide/src/format/markdown.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,33 @@ Example:
240240
This makes the level 1 heading with the content `Example heading`, ID `first`, and classes `class1` and `class2`. Note that the attributes should be space-separated.
241241

242242
More information can be found in the [heading attrs spec page](https://github.com/raphlinus/pulldown-cmark/blob/master/pulldown-cmark/specs/heading_attrs.txt).
243+
244+
### Definition lists
245+
246+
Definition lists can be used for things like glossary entries. The term is listed on a line by itself, followed by one or more definitions. Each definition must begin with a `:` (after 0-2 spaces).
247+
248+
Example:
249+
250+
```md
251+
term A
252+
: This is a definition of term A. Text
253+
can span multiple lines.
254+
255+
term B
256+
: This is a definition of term B.
257+
: This has more than one definition.
258+
```
259+
260+
This will render as:
261+
262+
term A
263+
: This is a definition of term A. Text
264+
can span multiple lines.
265+
266+
term B
267+
: This is a definition of term B.
268+
: This has more than one definition.
269+
270+
Terms are clickable just like headers, which will set the browser's URL to point directly to that term.
271+
272+
See the [definition lists spec](https://github.com/pulldown-cmark/pulldown-cmark/blob/HEAD/pulldown-cmark/specs/definition_lists.txt) for more information on the specifics of the syntax. See the [Wikipedia guidelines for glossaries](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Glossaries#General_guidelines_for_writing_glossaries) for some guidelines on how to write a glossary.

tests/testsuite/markdown.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,16 @@ fn smart_punctuation() {
146146
fn basic_markdown() {
147147
BookTest::from_dir("markdown/basic_markdown").check_all_main_files();
148148
}
149+
150+
#[test]
151+
fn definition_lists() {
152+
BookTest::from_dir("markdown/definition_lists")
153+
.check_all_main_files()
154+
.run("build", |cmd| {
155+
cmd.env("MDBOOK_OUTPUT__HTML__DEFINITION_LISTS", "false");
156+
})
157+
.check_main_file(
158+
"book/definition_lists.html",
159+
file!["markdown/definition_lists/expected_disabled/definition_lists.html"],
160+
);
161+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[book]
2+
title = "definition_lists"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<h1 id="definition-lists"><a class="header" href="#definition-lists">Definition Lists</a></h1>
2+
<dl>
3+
<dt id="apple"><a class="header" href="#apple">apple</a></dt>
4+
<dd>red fruit</dd>
5+
<dt id="orange"><a class="header" href="#orange">orange</a></dt>
6+
<dd>orange fruit</dd>
7+
<dt id="apple-1"><a class="header" href="#apple-1">apple</a></dt>
8+
<dd>red fruit</dd>
9+
<dd>computer company</dd>
10+
<dt id="orange-1"><a class="header" href="#orange-1">orange</a></dt>
11+
<dd>orange fruit</dd>
12+
<dd>telecom company</dd>
13+
<dt id="term"><a class="header" href="#term">term</a></dt>
14+
<dd>
15+
<ol>
16+
<li>
17+
<p>Para one</p>
18+
<p>Para two</p>
19+
</li>
20+
</ol>
21+
</dd>
22+
</dl>
23+
<h2 id="term-with-link"><a class="header" href="#term-with-link">Term with link</a></h2>
24+
<dl>
25+
<dt id="apple-2"><a class="header" href="#apple-2"><a href="some-page.html#apple">apple</a></a></dt>
26+
<dd>red fruit</dd>
27+
</dl>
28+
<h2 id="multi-line-term"><a class="header" href="#multi-line-term">Multi-line term</a></h2>
29+
<dl>
30+
<dt id="a-bc"><a class="header" href="#a-bc">a
31+
b<br>c</a></dt>
32+
<dd>
33+
<p>foo</p>
34+
</dd>
35+
</dl>
36+
<h2 id="nested"><a class="header" href="#nested">Nested</a></h2>
37+
<dl>
38+
<dt id="level-one"><a class="header" href="#level-one">level one</a></dt>
39+
<dd>
40+
<dl>
41+
<dt id="l1-level-two"><a class="header" href="#l1-level-two">l1
42+
level two</a></dt>
43+
<dd>
44+
<dl>
45+
<dt id="l2-level-three"><a class="header" href="#l2-level-three">l2
46+
level three</a></dt>
47+
<dd>l3</dd>
48+
</dl>
49+
</dd>
50+
</dl>
51+
</dd>
52+
<dt id="level-one-1"><a class="header" href="#level-one-1">level one</a></dt>
53+
<dd>l1</dd>
54+
</dl>
55+
<h2 id="loose"><a class="header" href="#loose">Loose</a></h2>
56+
<dl>
57+
<dt id="apple-3"><a class="header" href="#apple-3">apple</a></dt>
58+
<dd>
59+
<p>red fruit</p>
60+
</dd>
61+
<dd>
62+
<p>computer company</p>
63+
</dd>
64+
<dt id="orange-2"><a class="header" href="#orange-2">orange</a></dt>
65+
<dd>
66+
<p>orange fruit</p>
67+
</dd>
68+
</dl>

0 commit comments

Comments
 (0)