Documents: nested code tags stay literal; an indented \[ formula is block
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
5 files changed
+26
-12
No files matched your search
@@ -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]) {
|
fn keep(container: &mdast::Node, text: &str, kept: &mut [bool]) {
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
walk(container, &mut nodes);
|
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..] {
|
for node in &nodes[1..] {
|
||||||
use mdast::Node::*;
|
use mdast::Node::*;
|
||||||
let whole = span(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 {
|
let Some(tag) = html::tag(&text[whole.clone()]) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match &code {
|
if html::inline_style(&tag.name) != Some(Style::Code) {
|
||||||
Some((name, from)) if tag.closing && tag.name == *name => {
|
continue;
|
||||||
kept[*from..whole.start].fill(true);
|
}
|
||||||
code = None;
|
if !tag.closing {
|
||||||
|
if open.is_empty() {
|
||||||
|
from = whole.end;
|
||||||
}
|
}
|
||||||
None if !tag.closing && html::inline_style(&tag.name) == Some(Style::Code) => {
|
open.push(tag.name);
|
||||||
code = Some((tag.name, whole.end));
|
} 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 `<code>` runs to its block's end.
|
// An unclosed `<code>` runs to its block's end.
|
||||||
if let Some((_, from)) = code {
|
if !open.is_empty() {
|
||||||
kept[from..span(container).end].fill(true);
|
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), "$")
|
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) {
|
fn trim(node: &mut mdast::Node, swapped: &Swapped) {
|
||||||
let mdast::Node::Text(text) = node else {
|
let mdast::Node::Text(text) = node else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ fn code_and_tags_stay_literal() {
|
|||||||
untouched(r"a <code>\(x\)</code> b");
|
untouched(r"a <code>\(x\)</code> b");
|
||||||
untouched(r"a <kbd>\(x\)</kbd> b");
|
untouched(r"a <kbd>\(x\)</kbd> b");
|
||||||
untouched(r"a <code>\(x\) b");
|
untouched(r"a <code>\(x\) b");
|
||||||
|
untouched(r"a <code><code>\(x\)</code> \(y\)</code> b");
|
||||||
|
untouched(r"a <kbd><code>\(x\)</code> \(y\)</kbd> b");
|
||||||
|
assert_eq!(tex(r"<code>a</code> \(x\)"), ["x"], "after code, prose");
|
||||||
untouched(r"`\(x\)`");
|
untouched(r"`\(x\)`");
|
||||||
untouched(r"\(a `b` c\)");
|
untouched(r"\(a `b` c\)");
|
||||||
assert_eq!(tex(r"<b>\(x\)</b>"), ["x"], "other tags hold prose");
|
assert_eq!(tex(r"<b>\(x\)</b>"), ["x"], "other tags hold prose");
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ impl<'a> Reader<'a> {
|
|||||||
if let [mdast::Node::InlineMath(math)] = children
|
if let [mdast::Node::InlineMath(math)] = children
|
||||||
&& ["$$", "\\["]
|
&& ["$$", "\\["]
|
||||||
.iter()
|
.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()));
|
return out.push(Node::Math(math.value.trim().to_string()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,6 +421,12 @@ fn the_first_shortcode_decides_its_text_node() {
|
|||||||
fn latex_delimiters_read_as_math() {
|
fn latex_delimiters_read_as_math() {
|
||||||
assert_eq!(parse(r"\[c = 1\]"), [Node::Math("c = 1".into())]);
|
assert_eq!(parse(r"\[c = 1\]"), [Node::Math("c = 1".into())]);
|
||||||
assert_eq!(parse("\\[\nc = 1\n\\]"), [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 {
|
let Node::Paragraph(inline) = &parse(r"so \(c\) holds")[0] else {
|
||||||
panic!("inline math stays in its paragraph")
|
panic!("inline math stays in its paragraph")
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ fn framed(svg: &str, width: f32, height: f32) -> Result<(String, Size<Pixels>),
|
|||||||
Ok((wrapped(svg, [left, top, w, h])?, size(px(w), px(h))))
|
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<String, MathError> {
|
fn wrapped(svg: &str, [left, top, w, h]: [f32; 4]) -> Result<String, MathError> {
|
||||||
let open = svg
|
let open = svg
|
||||||
.find('>')
|
.find('>')
|
||||||
|
|||||||
Reference in new issue
Block a user