#![cfg(feature = "mdx")]
use satteri_arena::{Arena, Mdast};
use satteri_ast::mdast::{decode_math_data, MdastNodeType};
use satteri_pulldown_cmark::arena_build::parse;
use satteri_pulldown_cmark::Options;
fn mdx_math_options() -> Options {
Options::ENABLE_MDX | Options::ENABLE_MATH
}
fn find_inline_math_value(arena: &Arena<Mdast>) -> Option<String> {
(0..arena.len() as u32)
.find(|&id| arena.get_node(id).node_type == MdastNodeType::InlineMath as u8)
.map(|id| {
let value = decode_math_data(arena.get_type_data(id)).value;
arena.get_str(value).to_string()
})
}
#[test]
fn inline_math_with_braces_is_not_parsed_as_expression() {
for src in ["$\\frac{-b}{2a}$", "$x{2b}y$", "$x{a-}y$", "$x{a b}y$"] {
let (_arena, errors) = parse(src, mdx_math_options());
assert!(errors.is_empty(), "{src:?} produced MDX errors: {errors:?}");
}
}
#[test]
fn inline_math_preserves_brace_content() {
let (arena, errors) = parse("$\\frac{-b}{2a}$", mdx_math_options());
assert!(errors.is_empty(), "errors: {errors:?}");
assert_eq!(
find_inline_math_value(&arena).as_deref(),
Some("\\frac{-b}{2a}"),
);
}
#[test]
fn mdx_expression_outside_math_still_validated() {
let (_arena, errors) = parse("text {1 +} more", mdx_math_options());
assert!(
!errors.is_empty(),
"malformed expression should still error"
);
}
#[test]
fn inline_dollars_do_not_pair_across_display_fence() {
let (arena, errors) = parse("a$$\nx{1}y\n$$", mdx_math_options());
assert!(errors.is_empty(), "errors: {errors:?}");
let has_expr = (0..arena.len() as u32)
.any(|id| arena.get_node(id).node_type == MdastNodeType::MdxTextExpression as u8);
assert!(
has_expr,
"`{{1}}` after an inline `$$` must be a text expression, not math text"
);
}
#[test]
fn brace_in_span_closing_at_escaped_dollar_is_math_text() {
for src in ["e$}}_{\\$h", "$x{1 +}\\$", "a $b{1 +}c\\$"] {
let (_arena, errors) = parse(src, mdx_math_options());
assert!(errors.is_empty(), "{src:?} produced MDX errors: {errors:?}");
}
}