markdown_it/parser/block/
mod.rs

1//! Block rule chain
2mod state;
3pub use state::*;
4
5mod rule;
6pub use rule::*;
7
8#[doc(hidden)]
9pub mod builtin;
10
11use crate::common::ruler::Ruler;
12use crate::common::TypeKey;
13use crate::parser::extset::RootExtSet;
14use crate::parser::inline::InlineRoot;
15use crate::parser::node::NodeEmpty;
16use crate::{MarkdownIt, Node};
17
18type RuleFns = (
19    fn (&mut BlockState) -> Option<()>,
20    fn (&mut BlockState) -> Option<(Node, usize)>,
21);
22
23#[derive(Debug, Default)]
24/// Block-level tokenizer.
25pub struct BlockParser {
26    ruler: Ruler<TypeKey, RuleFns>,
27}
28
29impl BlockParser {
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Generate tokens for input range
35    ///
36    pub fn tokenize(&self, state: &mut BlockState) {
37        stacker::maybe_grow(64*1024, 1024*1024, || {
38            let mut has_empty_lines = false;
39
40            while state.line < state.line_max {
41                state.line = state.skip_empty_lines(state.line);
42                if state.line >= state.line_max { break; }
43
44                // Termination condition for nested calls.
45                // Nested calls currently used for blockquotes & lists
46                if state.line_indent(state.line) < 0 { break; }
47
48                // If nesting level exceeded - skip tail to the end. That's not ordinary
49                // situation and we should not care about content.
50                if state.level >= state.md.max_nesting {
51                    state.line = state.line_max;
52                    break;
53                }
54
55                // Try all possible rules.
56                // On success, rule should:
57                //
58                // - update `state.line`
59                // - update `state.tokens`
60                // - return true
61                let mut ok = None;
62
63                for rule in self.ruler.iter() {
64                    ok = rule.1(state);
65                    if ok.is_some() {
66                        break;
67                    }
68                }
69
70                if let Some((mut node, len)) = ok {
71                    state.line += len;
72                    if !node.is::<NodeEmpty>() {
73                        node.srcmap = state.get_map(state.line - len, state.line - 1);
74                        state.node.children.push(node);
75                    }
76                } else {
77                    // this can only happen if user disables paragraph rule
78                    // push text as is, this behavior can change in the future;
79                    // users should always have some kind of default block rule
80                    let mut content = state.get_line(state.line).to_owned();
81                    content.push('\n');
82                    let node = Node::new(InlineRoot::new(
83                        content,
84                        vec![(0, state.line_offsets[state.line].first_nonspace)],
85                    ));
86                    state.node.children.push(node);
87                    state.line += 1;
88                }
89
90                // set state.tight if we had an empty line before current tag
91                // i.e. latest empty line should not count
92                state.tight = !has_empty_lines;
93
94                // paragraph might "eat" one newline after it in nested lists
95                if state.is_empty(state.line - 1) {
96                    has_empty_lines = true;
97                }
98
99                if state.line < state.line_max && state.is_empty(state.line) {
100                    has_empty_lines = true;
101                    state.line += 1;
102                }
103            }
104        });
105    }
106
107    /// Process input string and push block tokens into `out_tokens`
108    ///
109    pub fn parse(&self, src: &str, node: Node, md: &MarkdownIt, root_ext: &mut RootExtSet) -> Node {
110        let mut state = BlockState::new(src, md, root_ext, node);
111        self.tokenize(&mut state);
112        state.node
113    }
114
115    pub fn add_rule<T: BlockRule>(&mut self) -> RuleBuilder<RuleFns> {
116        let item = self.ruler.add(TypeKey::of::<T>(), (T::check, T::run));
117        RuleBuilder::new(item)
118    }
119
120    pub fn has_rule<T: BlockRule>(&mut self) -> bool {
121        self.ruler.contains(TypeKey::of::<T>())
122    }
123
124    pub fn remove_rule<T: BlockRule>(&mut self) {
125        self.ruler.remove(TypeKey::of::<T>());
126    }
127}