nargo_document/plugin/
katex.rs1use crate::plugin::{DocumentPlugin, PluginContext, PluginMeta};
6use lazy_static::lazy_static;
7use regex::Regex;
8
9lazy_static! {
10 static ref BLOCK_MATH_RE: Regex = Regex::new(r"\$\$([\s\S]*?)\$\$").unwrap();
12 static ref INLINE_MATH_RE: Regex = Regex::new(r"\$([^$\n]+?)\$").unwrap();
14}
15
16pub struct KaTeXPlugin {
18 meta: PluginMeta,
20}
21
22impl KaTeXPlugin {
23 pub fn new() -> Self {
25 Self { meta: PluginMeta::new("nargo-document-plugin-katex".to_string(), "0.1.0".to_string(), "KaTeX 数学公式渲染插件,支持行内公式和块级公式".to_string()) }
26 }
27
28 fn process_block_math(&self, content: &str) -> String {
38 BLOCK_MATH_RE
39 .replace_all(content, |caps: ®ex::Captures| {
40 let math = &caps[1];
41 format!("<div class=\"katex-block\">{}</div>", math.trim())
42 })
43 .to_string()
44 }
45
46 fn process_inline_math(&self, content: &str) -> String {
56 let mut result = String::new();
57 let mut chars = content.chars().peekable();
58 let mut in_math = false;
59 let mut current_math = String::new();
60
61 while let Some(c) = chars.next() {
62 if c == '$' {
63 if let Some(&next) = chars.peek() {
64 if next == '$' {
65 chars.next();
66 if in_math {
67 result.push_str("$$");
68 result.push_str(¤t_math);
69 result.push_str("$$");
70 in_math = false;
71 current_math.clear();
72 }
73 else {
74 result.push_str("$$");
75 }
76 continue;
77 }
78 }
79
80 if in_math {
81 result.push_str(&format!("<span class=\"katex-inline\">{}</span>", current_math));
82 in_math = false;
83 current_math.clear();
84 }
85 else {
86 in_math = true;
87 }
88 }
89 else {
90 if in_math {
91 current_math.push(c);
92 }
93 else {
94 result.push(c);
95 }
96 }
97 }
98
99 if in_math {
100 result.push('$');
101 result.push_str(¤t_math);
102 }
103
104 result
105 }
106}
107
108impl Default for KaTeXPlugin {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl DocumentPlugin for KaTeXPlugin {
115 fn meta(&self) -> &PluginMeta {
117 &self.meta
118 }
119
120 fn before_render(&self, context: PluginContext) -> PluginContext {
130 let mut content = context.content;
131
132 content = self.process_block_math(&content);
133 content = self.process_inline_math(&content);
134
135 PluginContext { content, frontmatter: context.frontmatter, path: context.path }
136 }
137}