diff --git a/src/documents/markdown/tex.rs b/src/documents/markdown/tex.rs index fde53ab..f4b61f4 100644 --- a/src/documents/markdown/tex.rs +++ b/src/documents/markdown/tex.rs @@ -72,7 +72,8 @@ pub(super) fn swapped<'a>(text: &'a str, options: &ParseOptions) -> Swapped<'a> fn keep(container: &mdast::Node, text: &str, kept: &mut [bool]) { let mut nodes = Vec::new(); walk(container, &mut nodes); - let mut code: Option<(String, usize)> = None; + // Open code tags, and where the outermost one's text starts. + let (mut open, mut from) = (Vec::new(), 0); for node in &nodes[1..] { use mdast::Node::*; let whole = span(node); @@ -96,22 +97,26 @@ fn keep(container: &mdast::Node, text: &str, kept: &mut [bool]) { let Some(tag) = html::tag(&text[whole.clone()]) else { continue; }; - match &code { - Some((name, from)) if tag.closing && tag.name == *name => { - kept[*from..whole.start].fill(true); - code = None; + if html::inline_style(&tag.name) != Some(Style::Code) { + continue; + } + if !tag.closing { + if open.is_empty() { + from = whole.end; } - None if !tag.closing && html::inline_style(&tag.name) == Some(Style::Code) => { - code = Some((tag.name, whole.end)); + open.push(tag.name); + } else if let Some(ix) = open.iter().rposition(|name| *name == tag.name) { + open.remove(ix); + if open.is_empty() { + kept[from..whole.start].fill(true); } - _ => {} } } _ => {} } } // An unclosed `` runs to its block's end. - if let Some((_, from)) = code { + if !open.is_empty() { kept[from..span(container).end].fill(true); } } @@ -241,7 +246,7 @@ fn written_tex(value: &str, pair: &Pair, swapped: &Swapped, at: &Position) -> St tex.replace(char::from(swapped.stand_in), "$") } -/// Restores a text's dollars; drops a parting space at its ends. +/// Restores a text's dollars; drops parting spaces. fn trim(node: &mut mdast::Node, swapped: &Swapped) { let mdast::Node::Text(text) = node else { return; diff --git a/src/documents/markdown/tex/tests.rs b/src/documents/markdown/tex/tests.rs index b52911f..ecc02e6 100644 --- a/src/documents/markdown/tex/tests.rs +++ b/src/documents/markdown/tex/tests.rs @@ -83,6 +83,9 @@ fn code_and_tags_stay_literal() { untouched(r"a \(x\) b"); untouched(r"a \(x\) b"); untouched(r"a \(x\) b"); + untouched(r"a \(x\) \(y\) b"); + untouched(r"a \(x\) \(y\) b"); + assert_eq!(tex(r"a \(x\)"), ["x"], "after code, prose"); untouched(r"`\(x\)`"); untouched(r"\(a `b` c\)"); assert_eq!(tex(r"\(x\)"), ["x"], "other tags hold prose"); diff --git a/src/documents/tree/mod.rs b/src/documents/tree/mod.rs index 39be760..ac41d1e 100644 --- a/src/documents/tree/mod.rs +++ b/src/documents/tree/mod.rs @@ -323,7 +323,7 @@ impl<'a> Reader<'a> { if let [mdast::Node::InlineMath(math)] = children && ["$$", "\\["] .iter() - .any(|open| self.source[span(node)].starts_with(open)) + .any(|open| self.source[span(&children[0])].starts_with(open)) { return out.push(Node::Math(math.value.trim().to_string())); } diff --git a/src/documents/tree/tests.rs b/src/documents/tree/tests.rs index 8021190..fe23aac 100644 --- a/src/documents/tree/tests.rs +++ b/src/documents/tree/tests.rs @@ -421,6 +421,12 @@ fn the_first_shortcode_decides_its_text_node() { fn latex_delimiters_read_as_math() { assert_eq!(parse(r"\[c = 1\]"), [Node::Math("c = 1".into())]); assert_eq!(parse("\\[\nc = 1\n\\]"), [Node::Math("c = 1".into())]); + for indent in [" ", " ", " "] { + let source = format!(r"{indent}\[c = 1\]"); + assert_eq!(parse(&source), [Node::Math("c = 1".into())], "{source:?}"); + let source = format!("{indent}$$c = 1$$"); + assert_eq!(parse(&source), [Node::Math("c = 1".into())], "{source:?}"); + } let Node::Paragraph(inline) = &parse(r"so \(c\) holds")[0] else { panic!("inline math stays in its paragraph") }; diff --git a/src/typography/math.rs b/src/typography/math.rs index 947eadd..fb06337 100644 --- a/src/typography/math.rs +++ b/src/typography/math.rs @@ -193,7 +193,7 @@ fn framed(svg: &str, width: f32, height: f32) -> Result<(String, Size), Ok((wrapped(svg, [left, top, w, h])?, size(px(w), px(h)))) } -/// `svg` drawn in a px root viewing `[left, top, w, h]`. +/// `svg` in a px root viewing `[left, top, w, h]`. fn wrapped(svg: &str, [left, top, w, h]: [f32; 4]) -> Result { let open = svg .find('>')