markdown_it/plugins/cmark/block/
hr.rs

1//! Thematic breaks
2//!
3//! `***`, `---`, `___`
4//!
5//! <https://spec.commonmark.org/0.30/#thematic-breaks>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct ThematicBreak {
11    pub marker: char,
12    pub marker_len: usize,
13}
14
15impl NodeValue for ThematicBreak {
16    fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17        fmt.cr();
18        fmt.self_close("hr", &node.attrs);
19        fmt.cr();
20    }
21}
22
23pub fn add(md: &mut MarkdownIt) {
24    md.block.add_rule::<HrScanner>();
25}
26
27#[doc(hidden)]
28pub struct HrScanner;
29impl BlockRule for HrScanner {
30    fn run(state: &mut BlockState) -> Option<(Node, usize)> {
31
32        if state.line_indent(state.line) >= state.md.max_indent { return None; }
33
34        let mut chars = state.get_line(state.line).chars();
35
36        // Check hr marker
37        let marker = chars.next()?;
38        if marker != '*' && marker != '-' && marker != '_' { return None; }
39
40        // markers can be mixed with spaces, but there should be at least 3 of them
41        let mut cnt = 1;
42        for ch in chars {
43            if ch == marker {
44                cnt += 1;
45            } else if ch != ' ' && ch != '\t' {
46                return None;
47            }
48        }
49
50        if cnt < 3 { return None; }
51
52        let node = Node::new(ThematicBreak { marker, marker_len: cnt });
53        Some((node, 1))
54    }
55}