markdown_it_latex/
lib.rs

1mod latex;
2use crate::latex::latex_render_mathml;
3
4use markdown_it::{
5    MarkdownIt, Node, NodeValue, Renderer,
6    parser::inline::{ InlineRule, InlineState }
7};
8
9#[derive(Debug)]
10pub struct LaTeXNode {
11    latex: String,
12    inline: bool
13}
14
15impl NodeValue for LaTeXNode {
16    fn render(&self, _node: &Node, fmt: &mut dyn Renderer) {
17        let mathml = latex_render_mathml(&self.latex, self.inline);
18        fmt.text_raw(&mathml);
19    }
20}
21
22struct LaTeXScan;
23
24impl InlineRule for LaTeXScan {
25    const MARKER: char = '$';
26    fn run(state: &mut InlineState) -> Option<(Node, usize)> {
27        let input = &state.src[state.pos..];
28        if !input.starts_with("$") { return None; }
29        
30        let latex_enter_count = if input.starts_with("$$") { 2 } else { 1 };
31
32        let start_pos = state.pos + latex_enter_count;
33        let mut last_posi = state.pos + latex_enter_count;
34        let mut next_posi = state.pos + latex_enter_count;
35
36        while next_posi < state.pos_max {
37            let current_input = &state.src[next_posi..];
38            if !current_input.starts_with(&"$".repeat(latex_enter_count)) {
39                next_posi += 1;
40                continue;
41            }
42
43            last_posi = next_posi;
44            break;
45        }
46        let latex_text = &state.src[start_pos..last_posi];
47        Some((
48            Node::new(LaTeXNode {
49                latex: latex_text.to_string(),
50                inline: latex_enter_count == 1
51            }),
52            last_posi + latex_enter_count - (state.pos)
53        ))
54    }
55}
56
57pub fn add(md: &mut MarkdownIt) {
58    md.inline.add_rule::<LaTeXScan>();
59}