diff --git a/src/mdparser/preprocess.rs b/src/mdparser/preprocess.rs index 9702a89..d2d3158 100644 --- a/src/mdparser/preprocess.rs +++ b/src/mdparser/preprocess.rs @@ -51,6 +51,16 @@ pub static INLINE_MATH_DOLLAR_REGEX: Lazy = Lazy::new(|| { pub static DISPLAY_MATH_DOLLAR_REGEX: Lazy = Lazy::new(|| FancyRegex::new(r"(?s)^\$\$(?:\n)?(?P.*?)(? = + Lazy::new(|| FancyRegex::new(r"^\\\((?P.*?)(? = + Lazy::new(|| FancyRegex::new(r"(?s)^\\\[(?P.*?)(? = Lazy::new(|| Regex::new(r"【\d+】").unwrap()); // mirror SINGLE_BACKTICK_PATTERN from postprocess.py diff --git a/src/plugins/kagi_plugins/math_display.rs b/src/plugins/kagi_plugins/math_display.rs index e9c57f2..f3b659e 100644 --- a/src/plugins/kagi_plugins/math_display.rs +++ b/src/plugins/kagi_plugins/math_display.rs @@ -1,6 +1,7 @@ use crate::mdparser::inline::{InlineRule, InlineState}; -use crate::mdparser::preprocess::DISPLAY_MATH_DOLLAR_REGEX; +use crate::mdparser::preprocess::{DISPLAY_MATH_BRACKET_REGEX, DISPLAY_MATH_DOLLAR_REGEX}; use crate::plugin_config::DisplayMathExtensionPlugin; +use crate::plugins::cmark::inline::escape::EscapeScanner; use crate::plugins::kagi_plugins::math::{math_render, math_render_cached}; use crate::{MarkdownIt, Node, NodeValue, Renderer}; use html_escape::decode_html_entities; @@ -62,7 +63,49 @@ impl InlineRule for MathDisplayScanner { } } +struct MathDisplayBracketScanner; + +impl InlineRule for MathDisplayBracketScanner { + const MARKER: char = '\\'; + + fn run(state: &mut InlineState) -> Option<(Node, usize)> { + let input = &state.src[state.pos..state.pos_max]; + if !input.starts_with("\\[") { + return None; + } + + // reject if the opening backslash is itself escaped by a preceding backslash + let preceeding_char = state + .src + .get(..state.pos) + .and_then(|s| s.chars().rev().next()); + if let Some('\\') = preceeding_char { + return None; + } + + let config = state.md.ext.get::().unwrap(); + + if let Ok(Some(caps)) = DISPLAY_MATH_BRACKET_REGEX.captures(input) { + let complete_match = &caps[0]; + let math = caps.name("math")?.as_str(); + Some(( + Node::new(DisplayMath { + math: decode_html_entities(math).to_string(), + cache: config.cache, + }), + complete_match.len(), + )) + } else { + None + } + } +} + pub fn add(md: &mut MarkdownIt, config: DisplayMathExtensionPlugin) { md.ext.insert(config); md.inline.add_rule::(); + // must run before EscapeScanner, which would otherwise consume `\[` as a plain escape + md.inline + .add_rule::() + .before::(); } diff --git a/src/plugins/kagi_plugins/math_inline.rs b/src/plugins/kagi_plugins/math_inline.rs index d1c55aa..3bb6500 100644 --- a/src/plugins/kagi_plugins/math_inline.rs +++ b/src/plugins/kagi_plugins/math_inline.rs @@ -1,6 +1,7 @@ use crate::mdparser::inline::{InlineRule, InlineState}; -use crate::mdparser::preprocess::INLINE_MATH_DOLLAR_REGEX; +use crate::mdparser::preprocess::{INLINE_MATH_DOLLAR_REGEX, INLINE_MATH_PAREN_REGEX}; use crate::plugin_config::InlineMathExtensionPlugin; +use crate::plugins::cmark::inline::escape::EscapeScanner; use crate::plugins::kagi_plugins::math::{math_render, math_render_cached}; use crate::{MarkdownIt, Node, NodeValue, Renderer}; use html_escape::decode_html_entities; @@ -61,7 +62,49 @@ impl InlineRule for MathInlineScanner { } } +struct MathInlineParenScanner; + +impl InlineRule for MathInlineParenScanner { + const MARKER: char = '\\'; + + fn run(state: &mut InlineState) -> Option<(Node, usize)> { + let input = &state.src[state.pos..state.pos_max]; + if !input.starts_with("\\(") { + return None; + } + + // reject if the opening backslash is itself escaped by a preceding backslash + let preceeding_char = state + .src + .get(..state.pos) + .and_then(|s| s.chars().rev().next()); + if let Some('\\') = preceeding_char { + return None; + } + + let config = state.md.ext.get::().unwrap(); + + if let Ok(Some(caps)) = INLINE_MATH_PAREN_REGEX.captures(input) { + let complete_match = &caps[0]; + let math = caps.name("math")?.as_str(); + Some(( + Node::new(InlineMath { + math: decode_html_entities(math).to_string(), + cache: config.cache, + }), + complete_match.len(), + )) + } else { + None + } + } +} + pub fn add(md: &mut MarkdownIt, config: InlineMathExtensionPlugin) { md.ext.insert(config); md.inline.add_rule::(); + // must run before EscapeScanner, which would otherwise consume `\(` as a plain escape + md.inline + .add_rule::() + .before::(); }