markdown_it_sub/
lib.rs

1//! A [`markdown-it`](https://crates.io/crates/markdown-it) plugin to process subscript.
2//!
3//! To load the plugin:
4//!
5//! ```rust
6//! # use markdown_it;
7//! # use markdown_it_sub;
8//! let mut parser = markdown_it::MarkdownIt::new();
9//! markdown_it::plugins::cmark::add(&mut parser);
10//!
11//! markdown_it_sub::add(&mut parser);
12//!
13//! let html = parser.parse("log~2~(a)").xrender();
14//! assert_eq!(html, "<p>log<sub>2</sub>(a)</p>\n");
15//! ```
16
17use markdown_it::{generics::inline::emph_pair, MarkdownIt, Node, NodeValue, Renderer};
18
19#[derive(Debug)]
20pub struct Sub;
21
22impl NodeValue for Sub {
23    fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
24        fmt.open("sub", &node.attrs);
25        fmt.contents(&node.children);
26        fmt.close("sub");
27    }
28}
29
30/// Adds the subscript plugin to the parser.
31pub fn add(md: &mut MarkdownIt) {
32    emph_pair::add_with::<'~', 1, true>(md, || Node::new(Sub));
33}
34
35#[cfg(test)]
36mod test {
37    use super::add;
38    use markdown_it::{
39        plugins::{cmark, extra},
40        MarkdownIt,
41    };
42    use rstest::rstest;
43    use std::sync::LazyLock;
44
45    static MARKDOWN_PARSER: LazyLock<MarkdownIt> = LazyLock::new(|| {
46        let mut parser = MarkdownIt::new();
47        cmark::add(&mut parser);
48        extra::add(&mut parser);
49        add(&mut parser);
50
51        parser
52    });
53
54    #[rstest]
55    #[case("~foo~", "<p><sub>foo</sub></p>\n")]
56    #[case("foo~", "<p>foo~</p>\n")]
57    #[case("~foo", "<p>~foo</p>\n")]
58    #[case("foo~bar~", "<p>foo<sub>bar</sub></p>\n")]
59    #[case("~~foo~bar~~~", "<p><s>foo<sub>bar</sub></s></p>\n")]
60    #[case("\\~foo~", "<p>~foo~</p>\n")]
61    #[case("~foo\\~", "<p>~foo~</p>\n")]
62    fn test(#[case] md_str: &str, #[case] expected: &str) {
63        let result = MARKDOWN_PARSER.parse(md_str).xrender();
64
65        assert_eq!(result, String::from(expected));
66    }
67}