diff --git a/crates/mdbook-core/src/config.rs b/crates/mdbook-core/src/config.rs index a452702254..44de130301 100644 --- a/crates/mdbook-core/src/config.rs +++ b/crates/mdbook-core/src/config.rs @@ -434,6 +434,8 @@ pub struct HtmlConfig { pub preferred_dark_theme: Option, /// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash. pub smart_punctuation: bool, + /// Support for definition lists. + pub definition_lists: bool, /// Should mathjax be enabled? pub mathjax_support: bool, /// Additional CSS stylesheets to include in the rendered page's ``. @@ -501,6 +503,7 @@ impl Default for HtmlConfig { default_theme: None, preferred_dark_theme: None, smart_punctuation: true, + definition_lists: true, mathjax_support: false, additional_css: Vec::new(), additional_js: Vec::new(), diff --git a/crates/mdbook-html/front-end/css/general.css b/crates/mdbook-html/front-end/css/general.css index faa4a7323c..eca0e9da3b 100644 --- a/crates/mdbook-html/front-end/css/general.css +++ b/crates/mdbook-html/front-end/css/general.css @@ -61,7 +61,8 @@ h2:target::before, h3:target::before, h4:target::before, h5:target::before, -h6:target::before { +h6:target::before, +dt:target::before { display: inline-block; content: "»"; margin-inline-start: -30px; @@ -285,3 +286,41 @@ sup { fill: currentColor; 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; +} diff --git a/crates/mdbook-html/src/html/mod.rs b/crates/mdbook-html/src/html/mod.rs index 65f28fa102..af1306ae23 100644 --- a/crates/mdbook-html/src/html/mod.rs +++ b/crates/mdbook-html/src/html/mod.rs @@ -50,6 +50,7 @@ impl<'a> HtmlRenderOptions<'a> { ) -> HtmlRenderOptions<'a> { let mut markdown_options = MarkdownOptions::default(); markdown_options.smart_punctuation = config.smart_punctuation; + markdown_options.definition_lists = config.definition_lists; HtmlRenderOptions { markdown_options, path, diff --git a/crates/mdbook-html/src/html/tree.rs b/crates/mdbook-html/src/html/tree.rs index 339cee8fc1..2efce9678a 100644 --- a/crates/mdbook-html/src/html/tree.rs +++ b/crates/mdbook-html/src/html/tree.rs @@ -818,12 +818,14 @@ where } /// This is used after parsing is complete to add a unique `id` attribute - /// to all header elements, and to also add an `` tag so that clicking - /// the header will set the current URL to that header's fragment. + /// to all header and dt elements, and to also add an `` tag so that + /// clicking the element will set the current URL to that element's + /// fragment. fn add_header_links(&mut self) { let mut id_counter = HashSet::new(); - let headings = - self.node_ids_for_tag(&|name| matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6")); + let headings = self.node_ids_for_tag(&|name| { + matches!(name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "dt") + }); for heading in headings { let node = self.tree.get(heading).unwrap(); let el = node.value().as_element().unwrap(); diff --git a/crates/mdbook-markdown/src/lib.rs b/crates/mdbook-markdown/src/lib.rs index 0b63b8d1b7..0f490ee1f8 100644 --- a/crates/mdbook-markdown/src/lib.rs +++ b/crates/mdbook-markdown/src/lib.rs @@ -24,12 +24,17 @@ pub struct MarkdownOptions { /// /// This is `true` by default. pub smart_punctuation: bool, + /// Enables definition lists. + /// + /// This is `true` by default. + pub definition_lists: bool, } impl Default for MarkdownOptions { fn default() -> MarkdownOptions { MarkdownOptions { 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 { opts.insert(Options::ENABLE_SMART_PUNCTUATION); } + if options.definition_lists { + opts.insert(Options::ENABLE_DEFINITION_LIST); + } Parser::new_ext(text, opts) } diff --git a/guide/src/format/configuration/renderers.md b/guide/src/format/configuration/renderers.md index 65a1ac77c6..6e9ccda7c1 100644 --- a/guide/src/format/configuration/renderers.md +++ b/guide/src/format/configuration/renderers.md @@ -98,6 +98,7 @@ theme = "my-theme" default-theme = "light" preferred-dark-theme = "navy" smart-punctuation = true +definition-lists = true mathjax-support = false additional-css = ["custom.css", "custom2.css"] 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. See [Smart Punctuation](../markdown.md#smart-punctuation). 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 `false`. - **additional-css:** If you need to slightly change the appearance of your book diff --git a/guide/src/format/markdown.md b/guide/src/format/markdown.md index fccfcccbbe..f6f58a891f 100644 --- a/guide/src/format/markdown.md +++ b/guide/src/format/markdown.md @@ -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. 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. diff --git a/tests/testsuite/markdown.rs b/tests/testsuite/markdown.rs index 7369c4577d..b2aae75cbd 100644 --- a/tests/testsuite/markdown.rs +++ b/tests/testsuite/markdown.rs @@ -146,3 +146,16 @@ fn smart_punctuation() { fn basic_markdown() { 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"], + ); +} diff --git a/tests/testsuite/markdown/definition_lists/book.toml b/tests/testsuite/markdown/definition_lists/book.toml new file mode 100644 index 0000000000..4cbc64b0e4 --- /dev/null +++ b/tests/testsuite/markdown/definition_lists/book.toml @@ -0,0 +1,2 @@ +[book] +title = "definition_lists" diff --git a/tests/testsuite/markdown/definition_lists/expected/definition_lists.html b/tests/testsuite/markdown/definition_lists/expected/definition_lists.html new file mode 100644 index 0000000000..c647ccb4dd --- /dev/null +++ b/tests/testsuite/markdown/definition_lists/expected/definition_lists.html @@ -0,0 +1,68 @@ +

Definition Lists

+
+
apple
+
red fruit
+
orange
+
orange fruit
+
apple
+
red fruit
+
computer company
+
orange
+
orange fruit
+
telecom company
+
term
+
+
    +
  1. +

    Para one

    +

    Para two

    +
  2. +
+
+
+ +
+
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

+
+
\ No newline at end of file diff --git a/tests/testsuite/markdown/definition_lists/expected_disabled/definition_lists.html b/tests/testsuite/markdown/definition_lists/expected_disabled/definition_lists.html new file mode 100644 index 0000000000..7da2c3feb3 --- /dev/null +++ b/tests/testsuite/markdown/definition_lists/expected_disabled/definition_lists.html @@ -0,0 +1,37 @@ +

Definition Lists

+

apple +: red fruit

+

orange +: orange fruit

+

apple +: red fruit +: computer company

+

orange +: orange fruit +: telecom company

+

term +: 1. Para one

+
   Para two
+
+ +

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

\ No newline at end of file diff --git a/tests/testsuite/markdown/definition_lists/src/SUMMARY.md b/tests/testsuite/markdown/definition_lists/src/SUMMARY.md new file mode 100644 index 0000000000..941e800019 --- /dev/null +++ b/tests/testsuite/markdown/definition_lists/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [Definition lists](./definition_lists.md) diff --git a/tests/testsuite/markdown/definition_lists/src/definition_lists.md b/tests/testsuite/markdown/definition_lists/src/definition_lists.md new file mode 100644 index 0000000000..4a1c07572d --- /dev/null +++ b/tests/testsuite/markdown/definition_lists/src/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