inline: apply word attribute when flushing event buf

fixes issue with e.g `[text]({.cls})` where attributes get immediately
applied to `[text](` where link should have priority.
This commit is contained in:
Noah Hellman 2023-02-19 18:28:00 +01:00
parent 08ef15655b
commit 9454a2e393
2 changed files with 59 additions and 31 deletions

View file

@ -63,7 +63,7 @@ pub enum EventKind {
Atom(Atom),
Str,
Whitespace,
Attributes,
Attributes { container: bool },
Placeholder,
}
@ -273,7 +273,7 @@ impl<'s> Parser<'s> {
if non_empty {
let e_attr = event_opener - 1;
self.events[e_attr] = Event {
kind: EventKind::Attributes,
kind: EventKind::Attributes { container: true },
span: span_attr,
};
}
@ -360,20 +360,7 @@ impl<'s> Parser<'s> {
.map_or(false, |e| e.kind == EventKind::Str);
if set_attr {
let i = self
.events
.iter()
.rposition(|e| e.kind != EventKind::Str)
.map_or(0, |i| i + 1);
let span_str = self.events[i]
.span
.union(self.events[self.events.len() - 1].span);
self.events.drain(i..);
self.push(EventKind::Attributes);
self.push_sp(EventKind::Enter(Span), span_str.empty_before());
self.push_sp(EventKind::Str, span_str);
self.push_sp(EventKind::Exit(Span), span_str.empty_after());
self.push(EventKind::Attributes { container: false });
} else {
self.push_sp(EventKind::Placeholder, self.input.span.empty_before());
}
@ -573,7 +560,7 @@ impl<'s> Parser<'s> {
if let Some((non_empty, span)) = self.input.ahead_attributes() {
if non_empty {
self.events[e_attr] = Event {
kind: EventKind::Attributes,
kind: EventKind::Attributes { container: true },
span,
};
}
@ -697,6 +684,13 @@ impl<'s> Parser<'s> {
let ev = self.events.pop_front().unwrap();
span = span.union(ev.span);
}
if matches!(
self.events.front().map(|ev| &ev.kind),
Some(EventKind::Attributes { container: false })
) {
self.apply_word_attributes(span)
} else {
Event {
kind: EventKind::Str,
span,
@ -704,6 +698,41 @@ impl<'s> Parser<'s> {
}
}
fn apply_word_attributes(&mut self, span_str: Span) -> Event {
if let Some(i) = span_str
.of(self.input.src)
.bytes()
.rposition(|c| c.is_ascii_whitespace())
{
let before = span_str.with_len(i + 1);
let word = span_str.skip(i + 1);
self.events.push_front(Event {
kind: EventKind::Str,
span: word,
});
Event {
kind: EventKind::Str,
span: before,
}
} else {
let attr = self.events.pop_front().unwrap();
self.events.push_front(Event {
kind: EventKind::Exit(Span),
span: span_str.empty_after(),
});
self.events.push_front(Event {
kind: EventKind::Str,
span: span_str,
});
self.events.push_front(Event {
kind: EventKind::Enter(Span),
span: span_str.empty_before(),
});
attr
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Directionality {
Uni,
@ -883,7 +912,7 @@ impl<'s> Iterator for Parser<'s> {
self.events.pop_front().and_then(|e| match e.kind {
EventKind::Str if e.span.is_empty() => self.next(),
EventKind::Str | EventKind::Whitespace => Some(self.merge_str_events(e.span)),
EventKind::Placeholder => self.next(),
EventKind::Placeholder | EventKind::Attributes { container: false } => self.next(),
_ => Some(e),
})
}
@ -967,7 +996,7 @@ mod test {
test_parse!(
"pre `raw`{#id} post",
(Str, "pre "),
(Attributes, "{#id}"),
(Attributes { container: true }, "{#id}"),
(Enter(Verbatim), "`"),
(Str, "raw"),
(Exit(Verbatim), "`"),
@ -1152,14 +1181,13 @@ mod test {
fn span_url_attr_unclosed() {
test_parse!(
"[text]({.cls}",
(Attributes, "{.cls}"),
(Attributes { container: false }, "{.cls}"),
(Enter(Span), ""),
(Str, "[text]("),
(Exit(Span), ""),
);
}
#[ignore = "broken"]
#[test]
fn span_url_attr_closed() {
test_parse!(
@ -1196,7 +1224,7 @@ mod test {
fn span_attr() {
test_parse!(
"[abc]{.def}",
(Attributes, "{.def}"),
(Attributes { container: true }, "{.def}"),
(Enter(Span), "["),
(Str, "abc"),
(Exit(Span), "]"),
@ -1301,7 +1329,7 @@ mod test {
fn container_attr() {
test_parse!(
"_abc def_{.attr}",
(Attributes, "{.attr}"),
(Attributes { container: true }, "{.attr}"),
(Enter(Emphasis), "_"),
(Str, "abc def"),
(Exit(Emphasis), "_"),
@ -1329,7 +1357,7 @@ mod test {
fn container_attr_multiple() {
test_parse!(
"_abc def_{.a}{.b}{.c} {.d}",
(Attributes, "{.a}{.b}{.c}"),
(Attributes { container: true }, "{.a}{.b}{.c}"),
(Enter(Emphasis), "_"),
(Str, "abc def"),
(Exit(Emphasis), "_"),
@ -1341,7 +1369,7 @@ mod test {
fn attr() {
test_parse!(
"word{a=b}",
(Attributes, "{a=b}"),
(Attributes { container: false }, "{a=b}"),
(Enter(Span), ""),
(Str, "word"),
(Exit(Span), ""),
@ -1349,7 +1377,7 @@ mod test {
test_parse!(
"some word{.a}{.b} with attrs",
(Str, "some "),
(Attributes, "{.a}{.b}"),
(Attributes { container: false }, "{.a}{.b}"),
(Enter(Span), ""),
(Str, "word"),
(Exit(Span), ""),

View file

@ -799,7 +799,7 @@ impl<'s> Parser<'s> {
let mut first_is_attr = false;
let mut attributes = inline.as_ref().map_or_else(Attributes::new, |inl| {
if let inline::EventKind::Attributes = inl.kind {
if let inline::EventKind::Attributes { .. } = inl.kind {
first_is_attr = true;
attr::parse(inl.span.of(self.src))
} else {
@ -911,7 +911,7 @@ impl<'s> Parser<'s> {
},
inline::EventKind::Str => Event::Str(inline.span.of(self.src).into()),
inline::EventKind::Whitespace
| inline::EventKind::Attributes
| inline::EventKind::Attributes { .. }
| inline::EventKind::Placeholder => {
panic!("{:?}", inline)
}