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 https://github.com/rust-lang/mdBook/issues/2770
This commit is contained in:
parent
53d39a8654
commit
ba4c3ed873
13 changed files with 269 additions and 5 deletions
|
|
@ -434,6 +434,8 @@ pub struct HtmlConfig {
|
||||||
pub preferred_dark_theme: Option<String>,
|
pub preferred_dark_theme: Option<String>,
|
||||||
/// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash.
|
/// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash.
|
||||||
pub smart_punctuation: bool,
|
pub smart_punctuation: bool,
|
||||||
|
/// Support for definition lists.
|
||||||
|
pub definition_lists: bool,
|
||||||
/// Should mathjax be enabled?
|
/// Should mathjax be enabled?
|
||||||
pub mathjax_support: bool,
|
pub mathjax_support: bool,
|
||||||
/// Additional CSS stylesheets to include in the rendered page's `<head>`.
|
/// Additional CSS stylesheets to include in the rendered page's `<head>`.
|
||||||
|
|
@ -501,6 +503,7 @@ impl Default for HtmlConfig {
|
||||||
default_theme: None,
|
default_theme: None,
|
||||||
preferred_dark_theme: None,
|
preferred_dark_theme: None,
|
||||||
smart_punctuation: true,
|
smart_punctuation: true,
|
||||||
|
definition_lists: true,
|
||||||
mathjax_support: false,
|
mathjax_support: false,
|
||||||
additional_css: Vec::new(),
|
additional_css: Vec::new(),
|
||||||
additional_js: Vec::new(),
|
additional_js: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ h2:target::before,
|
||||||
h3:target::before,
|
h3:target::before,
|
||||||
h4:target::before,
|
h4:target::before,
|
||||||
h5:target::before,
|
h5:target::before,
|
||||||
h6:target::before {
|
h6:target::before,
|
||||||
|
dt:target::before {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
content: "»";
|
content: "»";
|
||||||
margin-inline-start: -30px;
|
margin-inline-start: -30px;
|
||||||
|
|
@ -285,3 +286,41 @@ sup {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
margin-bottom: -0.1em;
|
margin-bottom: -0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This uses a CSS counter to add numbers to definitions, but only if there is
|
||||||
|
more than one definition. */
|
||||||
|
dl, dt {
|
||||||
|
counter-reset: dd-counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When there is more than one definition, increment the counter. The first
|
||||||
|
selector selects the first definition, and the second one selects definitions
|
||||||
|
2 and beyond.*/
|
||||||
|
dd:has(+ dd), dd + dd {
|
||||||
|
counter-increment: dd-counter;
|
||||||
|
/* Use flex display to help with positioning the numbers when there is a p
|
||||||
|
tag inside the definition. */
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shows the counter for definitions. The first selector selects the first
|
||||||
|
definition, and the second one selections definitions 2 and beyond.*/
|
||||||
|
dd:has(+ dd)::before, dd + dd::before {
|
||||||
|
content: counter(dd-counter) ". ";
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd > p {
|
||||||
|
/* For loose definitions that have a p tag inside, don't add a bunch of
|
||||||
|
space before the definition. */
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ impl<'a> HtmlRenderOptions<'a> {
|
||||||
) -> HtmlRenderOptions<'a> {
|
) -> HtmlRenderOptions<'a> {
|
||||||
let mut markdown_options = MarkdownOptions::default();
|
let mut markdown_options = MarkdownOptions::default();
|
||||||
markdown_options.smart_punctuation = config.smart_punctuation;
|
markdown_options.smart_punctuation = config.smart_punctuation;
|
||||||
|
markdown_options.definition_lists = config.definition_lists;
|
||||||
HtmlRenderOptions {
|
HtmlRenderOptions {
|
||||||
markdown_options,
|
markdown_options,
|
||||||
path,
|
path,
|
||||||
|
|
|
||||||
|
|
@ -818,12 +818,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used after parsing is complete to add a unique `id` attribute
|
/// This is used after parsing is complete to add a unique `id` attribute
|
||||||
/// to all header elements, and to also add an `<a>` tag so that clicking
|
/// to all header and dt elements, and to also add an `<a>` tag so that
|
||||||
/// the header will set the current URL to that header's fragment.
|
/// clicking the element will set the current URL to that element's
|
||||||
|
/// fragment.
|
||||||
fn add_header_links(&mut self) {
|
fn add_header_links(&mut self) {
|
||||||
let mut id_counter = HashSet::new();
|
let mut id_counter = HashSet::new();
|
||||||
let headings =
|
let headings = self.node_ids_for_tag(&|name| {
|
||||||
self.node_ids_for_tag(&|name| matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6"));
|
matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "dt")
|
||||||
|
});
|
||||||
for heading in headings {
|
for heading in headings {
|
||||||
let node = self.tree.get(heading).unwrap();
|
let node = self.tree.get(heading).unwrap();
|
||||||
let el = node.value().as_element().unwrap();
|
let el = node.value().as_element().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,17 @@ pub struct MarkdownOptions {
|
||||||
///
|
///
|
||||||
/// This is `true` by default.
|
/// This is `true` by default.
|
||||||
pub smart_punctuation: bool,
|
pub smart_punctuation: bool,
|
||||||
|
/// Enables definition lists.
|
||||||
|
///
|
||||||
|
/// This is `true` by default.
|
||||||
|
pub definition_lists: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MarkdownOptions {
|
impl Default for MarkdownOptions {
|
||||||
fn default() -> MarkdownOptions {
|
fn default() -> MarkdownOptions {
|
||||||
MarkdownOptions {
|
MarkdownOptions {
|
||||||
smart_punctuation: true,
|
smart_punctuation: true,
|
||||||
|
definition_lists: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,5 +50,8 @@ pub fn new_cmark_parser<'text>(text: &'text str, options: &MarkdownOptions) -> P
|
||||||
if options.smart_punctuation {
|
if options.smart_punctuation {
|
||||||
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
|
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||||
}
|
}
|
||||||
|
if options.definition_lists {
|
||||||
|
opts.insert(Options::ENABLE_DEFINITION_LIST);
|
||||||
|
}
|
||||||
Parser::new_ext(text, opts)
|
Parser::new_ext(text, opts)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ theme = "my-theme"
|
||||||
default-theme = "light"
|
default-theme = "light"
|
||||||
preferred-dark-theme = "navy"
|
preferred-dark-theme = "navy"
|
||||||
smart-punctuation = true
|
smart-punctuation = true
|
||||||
|
definition-lists = true
|
||||||
mathjax-support = false
|
mathjax-support = false
|
||||||
additional-css = ["custom.css", "custom2.css"]
|
additional-css = ["custom.css", "custom2.css"]
|
||||||
additional-js = ["custom.js"]
|
additional-js = ["custom.js"]
|
||||||
|
|
@ -125,6 +126,7 @@ The following configuration options are available:
|
||||||
- **smart-punctuation:** Converts quotes to curly quotes, `...` to `…`, `--` to en-dash, and `---` to em-dash.
|
- **smart-punctuation:** Converts quotes to curly quotes, `...` to `…`, `--` to en-dash, and `---` to em-dash.
|
||||||
See [Smart Punctuation](../markdown.md#smart-punctuation).
|
See [Smart Punctuation](../markdown.md#smart-punctuation).
|
||||||
Defaults to `true`.
|
Defaults to `true`.
|
||||||
|
- **definition-lists:** Enables [definition lists](../markdown.md#definition-lists). Defaults to `true`.
|
||||||
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
|
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
|
||||||
`false`.
|
`false`.
|
||||||
- **additional-css:** If you need to slightly change the appearance of your book
|
- **additional-css:** If you need to slightly change the appearance of your book
|
||||||
|
|
|
||||||
|
|
@ -240,3 +240,33 @@ Example:
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
|
### Definition lists
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```md
|
||||||
|
term A
|
||||||
|
: This is a definition of term A. Text
|
||||||
|
can span multiple lines.
|
||||||
|
|
||||||
|
term B
|
||||||
|
: This is a definition of term B.
|
||||||
|
: This has more than one definition.
|
||||||
|
```
|
||||||
|
|
||||||
|
This will render as:
|
||||||
|
|
||||||
|
term A
|
||||||
|
: This is a definition of term A. Text
|
||||||
|
can span multiple lines.
|
||||||
|
|
||||||
|
term B
|
||||||
|
: This is a definition of term B.
|
||||||
|
: This has more than one definition.
|
||||||
|
|
||||||
|
Terms are clickable just like headers, which will set the browser's URL to point directly to that term.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
||||||
|
|
@ -146,3 +146,16 @@ fn smart_punctuation() {
|
||||||
fn basic_markdown() {
|
fn basic_markdown() {
|
||||||
BookTest::from_dir("markdown/basic_markdown").check_all_main_files();
|
BookTest::from_dir("markdown/basic_markdown").check_all_main_files();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn definition_lists() {
|
||||||
|
BookTest::from_dir("markdown/definition_lists")
|
||||||
|
.check_all_main_files()
|
||||||
|
.run("build", |cmd| {
|
||||||
|
cmd.env("MDBOOK_OUTPUT__HTML__DEFINITION_LISTS", "false");
|
||||||
|
})
|
||||||
|
.check_main_file(
|
||||||
|
"book/definition_lists.html",
|
||||||
|
file!["markdown/definition_lists/expected_disabled/definition_lists.html"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
2
tests/testsuite/markdown/definition_lists/book.toml
Normal file
2
tests/testsuite/markdown/definition_lists/book.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[book]
|
||||||
|
title = "definition_lists"
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<h1 id="definition-lists"><a class="header" href="#definition-lists">Definition Lists</a></h1>
|
||||||
|
<dl>
|
||||||
|
<dt id="apple"><a class="header" href="#apple">apple</a></dt>
|
||||||
|
<dd>red fruit</dd>
|
||||||
|
<dt id="orange"><a class="header" href="#orange">orange</a></dt>
|
||||||
|
<dd>orange fruit</dd>
|
||||||
|
<dt id="apple-1"><a class="header" href="#apple-1">apple</a></dt>
|
||||||
|
<dd>red fruit</dd>
|
||||||
|
<dd>computer company</dd>
|
||||||
|
<dt id="orange-1"><a class="header" href="#orange-1">orange</a></dt>
|
||||||
|
<dd>orange fruit</dd>
|
||||||
|
<dd>telecom company</dd>
|
||||||
|
<dt id="term"><a class="header" href="#term">term</a></dt>
|
||||||
|
<dd>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>Para one</p>
|
||||||
|
<p>Para two</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<h2 id="term-with-link"><a class="header" href="#term-with-link">Term with link</a></h2>
|
||||||
|
<dl>
|
||||||
|
<dt id="apple-2"><a class="header" href="#apple-2"><a href="some-page.html#apple">apple</a></a></dt>
|
||||||
|
<dd>red fruit</dd>
|
||||||
|
</dl>
|
||||||
|
<h2 id="multi-line-term"><a class="header" href="#multi-line-term">Multi-line term</a></h2>
|
||||||
|
<dl>
|
||||||
|
<dt id="a-bc"><a class="header" href="#a-bc">a
|
||||||
|
b<br>c</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p>foo</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<h2 id="nested"><a class="header" href="#nested">Nested</a></h2>
|
||||||
|
<dl>
|
||||||
|
<dt id="level-one"><a class="header" href="#level-one">level one</a></dt>
|
||||||
|
<dd>
|
||||||
|
<dl>
|
||||||
|
<dt id="l1-level-two"><a class="header" href="#l1-level-two">l1
|
||||||
|
level two</a></dt>
|
||||||
|
<dd>
|
||||||
|
<dl>
|
||||||
|
<dt id="l2-level-three"><a class="header" href="#l2-level-three">l2
|
||||||
|
level three</a></dt>
|
||||||
|
<dd>l3</dd>
|
||||||
|
</dl>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd>
|
||||||
|
<dt id="level-one-1"><a class="header" href="#level-one-1">level one</a></dt>
|
||||||
|
<dd>l1</dd>
|
||||||
|
</dl>
|
||||||
|
<h2 id="loose"><a class="header" href="#loose">Loose</a></h2>
|
||||||
|
<dl>
|
||||||
|
<dt id="apple-3"><a class="header" href="#apple-3">apple</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p>red fruit</p>
|
||||||
|
</dd>
|
||||||
|
<dd>
|
||||||
|
<p>computer company</p>
|
||||||
|
</dd>
|
||||||
|
<dt id="orange-2"><a class="header" href="#orange-2">orange</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p>orange fruit</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<h1 id="definition-lists"><a class="header" href="#definition-lists">Definition Lists</a></h1>
|
||||||
|
<p>apple
|
||||||
|
: red fruit</p>
|
||||||
|
<p>orange
|
||||||
|
: orange fruit</p>
|
||||||
|
<p>apple
|
||||||
|
: red fruit
|
||||||
|
: computer company</p>
|
||||||
|
<p>orange
|
||||||
|
: orange fruit
|
||||||
|
: telecom company</p>
|
||||||
|
<p>term
|
||||||
|
: 1. Para one</p>
|
||||||
|
<pre><code> Para two
|
||||||
|
</code></pre>
|
||||||
|
<h2 id="term-with-link"><a class="header" href="#term-with-link">Term with link</a></h2>
|
||||||
|
<p><a href="some-page.html#apple">apple</a>
|
||||||
|
: red fruit</p>
|
||||||
|
<h2 id="multi-line-term"><a class="header" href="#multi-line-term">Multi-line term</a></h2>
|
||||||
|
<p>a
|
||||||
|
b<br>c</p>
|
||||||
|
<p>: foo</p>
|
||||||
|
<h2 id="nested"><a class="header" href="#nested">Nested</a></h2>
|
||||||
|
<p>level one
|
||||||
|
: l1
|
||||||
|
level two
|
||||||
|
: l2
|
||||||
|
level three
|
||||||
|
: l3</p>
|
||||||
|
<p>level one
|
||||||
|
: l1</p>
|
||||||
|
<h2 id="loose"><a class="header" href="#loose">Loose</a></h2>
|
||||||
|
<p>apple</p>
|
||||||
|
<p>: red fruit
|
||||||
|
: computer company</p>
|
||||||
|
<p>orange</p>
|
||||||
|
<p>: orange fruit</p>
|
||||||
3
tests/testsuite/markdown/definition_lists/src/SUMMARY.md
Normal file
3
tests/testsuite/markdown/definition_lists/src/SUMMARY.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
- [Definition lists](./definition_lists.md)
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Definition Lists
|
||||||
|
|
||||||
|
apple
|
||||||
|
: red fruit
|
||||||
|
|
||||||
|
orange
|
||||||
|
: orange fruit
|
||||||
|
|
||||||
|
apple
|
||||||
|
: red fruit
|
||||||
|
: computer company
|
||||||
|
|
||||||
|
orange
|
||||||
|
: orange fruit
|
||||||
|
: telecom company
|
||||||
|
|
||||||
|
term
|
||||||
|
: 1. Para one
|
||||||
|
|
||||||
|
Para two
|
||||||
|
|
||||||
|
## Term with link
|
||||||
|
|
||||||
|
[apple](some-page.md#apple)
|
||||||
|
: red fruit
|
||||||
|
|
||||||
|
## Multi-line term
|
||||||
|
|
||||||
|
a
|
||||||
|
b\
|
||||||
|
c
|
||||||
|
|
||||||
|
: foo
|
||||||
|
|
||||||
|
## Nested
|
||||||
|
|
||||||
|
level one
|
||||||
|
: l1
|
||||||
|
level two
|
||||||
|
: l2
|
||||||
|
level three
|
||||||
|
: l3
|
||||||
|
|
||||||
|
level one
|
||||||
|
: l1
|
||||||
|
|
||||||
|
## Loose
|
||||||
|
|
||||||
|
apple
|
||||||
|
|
||||||
|
: red fruit
|
||||||
|
: computer company
|
||||||
|
|
||||||
|
orange
|
||||||
|
|
||||||
|
: orange fruit
|
||||||
Loading…
Add table
Reference in a new issue