use crate::plugin::{DocumentPlugin, PluginContext, PluginMeta};
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref BLOCK_MATH_RE: Regex = Regex::new(r"\$\$([\s\S]*?)\$\$").unwrap();
static ref INLINE_MATH_RE: Regex = Regex::new(r"\$([^$\n]+?)\$").unwrap();
}
pub struct KaTeXPlugin {
meta: PluginMeta,
}
impl KaTeXPlugin {
pub fn new() -> Self {
Self { meta: PluginMeta::new("nargo-document-plugin-katex".to_string(), "0.1.0".to_string(), "KaTeX 数学公式渲染插件,支持行内公式和块级公式".to_string()) }
}
fn process_block_math(&self, content: &str) -> String {
BLOCK_MATH_RE
.replace_all(content, |caps: ®ex::Captures| {
let math = &caps[1];
format!("<div class=\"katex-block\">{}</div>", math.trim())
})
.to_string()
}
fn process_inline_math(&self, content: &str) -> String {
let mut result = String::new();
let mut chars = content.chars().peekable();
let mut in_math = false;
let mut current_math = String::new();
while let Some(c) = chars.next() {
if c == '$' {
if let Some(&next) = chars.peek() {
if next == '$' {
chars.next();
if in_math {
result.push_str("$$");
result.push_str(¤t_math);
result.push_str("$$");
in_math = false;
current_math.clear();
}
else {
result.push_str("$$");
}
continue;
}
}
if in_math {
result.push_str(&format!("<span class=\"katex-inline\">{}</span>", current_math));
in_math = false;
current_math.clear();
}
else {
in_math = true;
}
}
else {
if in_math {
current_math.push(c);
}
else {
result.push(c);
}
}
}
if in_math {
result.push('$');
result.push_str(¤t_math);
}
result
}
}
impl Default for KaTeXPlugin {
fn default() -> Self {
Self::new()
}
}
impl DocumentPlugin for KaTeXPlugin {
fn meta(&self) -> &PluginMeta {
&self.meta
}
fn before_render(&self, context: PluginContext) -> PluginContext {
let mut content = context.content;
content = self.process_block_math(&content);
content = self.process_inline_math(&content);
PluginContext { content, frontmatter: context.frontmatter, path: context.path }
}
}