inline: parse multiline attributes
reimplement after broken by "take str per line instead of full inline iter" commit also resolves #18 and #34
This commit is contained in:
parent
3d42820001
commit
62d33effc4
4 changed files with 351 additions and 112 deletions
33
src/attr.rs
33
src/attr.rs
|
@ -239,6 +239,37 @@ impl<'s> std::fmt::Debug for Attributes<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Validator {
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: State::Start,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restart(&mut self) {
|
||||
self.state = State::Start;
|
||||
}
|
||||
|
||||
/// Returns number of valid bytes parsed (0 means invalid) if finished, otherwise more input is
|
||||
/// needed.
|
||||
pub fn parse(&mut self, input: &str) -> Option<usize> {
|
||||
let mut chars = input.chars();
|
||||
for c in &mut chars {
|
||||
self.state = self.state.step(c);
|
||||
match self.state {
|
||||
State::Done => return Some(input.len() - chars.as_str().len()),
|
||||
State::Invalid => return Some(0),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes parser, take input of one or more consecutive attributes and create an `Attributes`
|
||||
/// object.
|
||||
///
|
||||
|
@ -302,7 +333,7 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
fn finish(self) -> Attributes<'s> {
|
||||
pub fn finish(self) -> Attributes<'s> {
|
||||
self.attrs
|
||||
}
|
||||
}
|
||||
|
|
328
src/inline.rs
328
src/inline.rs
|
@ -57,18 +57,21 @@ pub enum QuoteType {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EventKind {
|
||||
pub enum EventKind<'s> {
|
||||
Enter(Container),
|
||||
Exit(Container),
|
||||
Atom(Atom),
|
||||
Str,
|
||||
Attributes { container: bool },
|
||||
Attributes {
|
||||
container: bool,
|
||||
attrs: attr::Attributes<'s>,
|
||||
},
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Event {
|
||||
pub kind: EventKind,
|
||||
pub struct Event<'s> {
|
||||
pub kind: EventKind<'s>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
|
@ -146,25 +149,6 @@ impl<'s> Input<'s> {
|
|||
self.span = self.span.empty_after();
|
||||
}
|
||||
|
||||
fn ahead_attributes(&mut self) -> Option<(bool, Span)> {
|
||||
let mut span = self.span.empty_after();
|
||||
let mut ahead = self.lexer.ahead().chars();
|
||||
let (mut attr_len, mut has_attr) = attr::valid(&mut ahead);
|
||||
if attr_len > 0 {
|
||||
while attr_len > 0 {
|
||||
span = span.extend(attr_len);
|
||||
self.lexer = lex::Lexer::new(ahead.as_str());
|
||||
|
||||
let (l, non_empty) = attr::valid(&mut ahead);
|
||||
has_attr |= non_empty;
|
||||
attr_len = l;
|
||||
}
|
||||
Some((has_attr, span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn ahead_raw_format(&mut self) -> Option<Span> {
|
||||
if matches!(
|
||||
self.lexer.peek().map(|t| &t.kind),
|
||||
|
@ -211,6 +195,12 @@ struct VerbatimState {
|
|||
non_whitespace_last: Option<(lex::Kind, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum AttributesElementType {
|
||||
Container { e_placeholder: usize },
|
||||
Word,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parser<'s> {
|
||||
input: Input<'s>,
|
||||
|
@ -218,9 +208,11 @@ pub struct Parser<'s> {
|
|||
openers: Vec<(Opener, usize)>,
|
||||
/// Buffer queue for next events. Events are buffered until no modifications due to future
|
||||
/// characters are needed.
|
||||
events: std::collections::VecDeque<Event>,
|
||||
events: std::collections::VecDeque<Event<'s>>,
|
||||
/// State if inside a verbatim container.
|
||||
verbatim: Option<VerbatimState>,
|
||||
/// State if currently parsing potential attributes.
|
||||
attributes: Option<AttributesElementType>,
|
||||
/// Storage of cow strs, used to reduce size of [`Container`].
|
||||
pub(crate) store_cowstrs: Vec<CowStr<'s>>,
|
||||
}
|
||||
|
@ -230,6 +222,9 @@ enum ControlFlow {
|
|||
Continue,
|
||||
/// Next line is needed to emit an event.
|
||||
Next,
|
||||
/// More lines are needed to emit an event. Unlike for the `Next` variant, the internal ahead
|
||||
/// buffer has already been examined, and more lines need to retrieved from the block parser.
|
||||
More,
|
||||
/// Parsing of the line is completed.
|
||||
Done,
|
||||
}
|
||||
|
@ -241,6 +236,7 @@ impl<'s> Parser<'s> {
|
|||
openers: Vec::new(),
|
||||
events: std::collections::VecDeque::new(),
|
||||
verbatim: None,
|
||||
attributes: None,
|
||||
store_cowstrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -253,25 +249,26 @@ impl<'s> Parser<'s> {
|
|||
debug_assert!(self.events.is_empty());
|
||||
self.input.reset();
|
||||
self.openers.clear();
|
||||
debug_assert!(self.events.is_empty());
|
||||
debug_assert!(self.attributes.is_none());
|
||||
debug_assert!(self.verbatim.is_none());
|
||||
self.store_cowstrs.clear();
|
||||
}
|
||||
|
||||
fn push_sp(&mut self, kind: EventKind, span: Span) -> Option<ControlFlow> {
|
||||
fn push_sp(&mut self, kind: EventKind<'s>, span: Span) -> Option<ControlFlow> {
|
||||
self.events.push_back(Event { kind, span });
|
||||
Some(Continue)
|
||||
}
|
||||
|
||||
fn push(&mut self, kind: EventKind) -> Option<ControlFlow> {
|
||||
fn push(&mut self, kind: EventKind<'s>) -> Option<ControlFlow> {
|
||||
self.push_sp(kind, self.input.span)
|
||||
}
|
||||
|
||||
fn parse_event(&mut self) -> ControlFlow {
|
||||
self.input.reset_span();
|
||||
|
||||
if let Some(first) = self.input.eat() {
|
||||
self.parse_verbatim(&first)
|
||||
.or_else(|| self.parse_attributes(&first))
|
||||
self.parse_attributes(&first)
|
||||
.or_else(|| self.parse_verbatim(&first))
|
||||
.or_else(|| self.parse_autolink(&first))
|
||||
.or_else(|| self.parse_symbol(&first))
|
||||
.or_else(|| self.parse_footnote_reference(&first))
|
||||
|
@ -305,15 +302,6 @@ impl<'s> Parser<'s> {
|
|||
self.events[event_opener].span = span_format;
|
||||
self.input.span = span_format.translate(1);
|
||||
span_closer = span_format;
|
||||
} else if let Some((non_empty, span_attr)) = self.input.ahead_attributes() {
|
||||
if non_empty {
|
||||
let e_attr = event_opener - 1;
|
||||
self.events[e_attr] = Event {
|
||||
kind: EventKind::Attributes { container: true },
|
||||
span: span_attr,
|
||||
};
|
||||
}
|
||||
self.input.span = span_attr;
|
||||
};
|
||||
let ty_opener = if let EventKind::Enter(ty) = self.events[event_opener].kind {
|
||||
debug_assert!(matches!(
|
||||
|
@ -330,6 +318,18 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
self.push_sp(EventKind::Exit(ty_opener), span_closer);
|
||||
self.verbatim = None;
|
||||
if raw_format.is_none()
|
||||
&& self.input.peek().map_or(false, |t| {
|
||||
matches!(t.kind, lex::Kind::Open(Delimiter::Brace))
|
||||
})
|
||||
{
|
||||
return self.ahead_attributes(
|
||||
AttributesElementType::Container {
|
||||
e_placeholder: event_opener - 1,
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// continue verbatim
|
||||
let is_whitespace = self
|
||||
|
@ -376,41 +376,123 @@ impl<'s> Parser<'s> {
|
|||
non_whitespace_encountered: false,
|
||||
non_whitespace_last: None,
|
||||
});
|
||||
self.attributes = None;
|
||||
self.push(EventKind::Enter(ty))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_attributes(&mut self, first: &lex::Token) -> Option<ControlFlow> {
|
||||
if first.kind == lex::Kind::Open(Delimiter::Brace) {
|
||||
let mut ahead = self.input.lexer.ahead().chars();
|
||||
let (mut attr_len, mut has_attr) = attr::valid(std::iter::once('{').chain(&mut ahead));
|
||||
attr_len = attr_len.saturating_sub(1); // rm {
|
||||
if attr_len > 0 {
|
||||
while attr_len > 0 {
|
||||
self.input.span = self.input.span.extend(attr_len);
|
||||
self.input.lexer = lex::Lexer::new(ahead.as_str());
|
||||
let elem_ty = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or(AttributesElementType::Word);
|
||||
self.ahead_attributes(elem_ty, true)
|
||||
} else {
|
||||
debug_assert!(self.attributes.is_none());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
let (l, non_empty) = attr::valid(&mut ahead);
|
||||
attr_len = l;
|
||||
has_attr |= non_empty;
|
||||
}
|
||||
fn ahead_attributes(
|
||||
&mut self,
|
||||
elem_ty: AttributesElementType,
|
||||
opener_eaten: bool,
|
||||
) -> Option<ControlFlow> {
|
||||
let start_attr = self.input.span.end() - usize::from(opener_eaten);
|
||||
debug_assert!(self.input.src[start_attr..].starts_with('{'));
|
||||
|
||||
let set_attr = has_attr
|
||||
&& self
|
||||
.events
|
||||
.back()
|
||||
.map_or(false, |e| e.kind == EventKind::Str);
|
||||
|
||||
if set_attr {
|
||||
self.push(EventKind::Attributes { container: false });
|
||||
let mut end_attr = start_attr;
|
||||
let mut line_next = 0;
|
||||
let mut valid_lines = 0;
|
||||
let mut line_end = self.input.span_line.end();
|
||||
{
|
||||
let mut line_start = start_attr;
|
||||
let mut validator = attr::Validator::new();
|
||||
let mut res = validator.parse(&self.input.src[line_start..line_end]);
|
||||
loop {
|
||||
if let Some(len) = res.take() {
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
valid_lines = line_next;
|
||||
end_attr = line_start + len;
|
||||
if self.input.src[end_attr..].starts_with('{') {
|
||||
line_start = end_attr;
|
||||
validator.restart();
|
||||
res = validator.parse(&self.input.src[end_attr..line_end]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if let Some(l) = self.input.ahead.get(line_next) {
|
||||
line_next += 1;
|
||||
line_start = l.start();
|
||||
line_end = l.end();
|
||||
res = validator.parse(l.of(self.input.src));
|
||||
} else if self.input.complete {
|
||||
// no need to ask for more input
|
||||
break;
|
||||
} else {
|
||||
self.push_sp(EventKind::Placeholder, self.input.span.empty_before());
|
||||
self.attributes = Some(elem_ty);
|
||||
if opener_eaten {
|
||||
self.input.span = Span::empty_at(start_attr);
|
||||
self.input.lexer = lex::Lexer::new(
|
||||
&self.input.src[start_attr..self.input.span_line.end()],
|
||||
);
|
||||
}
|
||||
return Some(More);
|
||||
}
|
||||
return Some(Continue);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
if start_attr == end_attr {
|
||||
return None;
|
||||
}
|
||||
|
||||
// retrieve attributes
|
||||
let attrs = {
|
||||
let first = Span::new(start_attr, self.input.span_line.end());
|
||||
let mut parser = attr::Parser::new(attr::Attributes::new());
|
||||
for line in
|
||||
std::iter::once(first).chain(self.input.ahead.iter().take(valid_lines).copied())
|
||||
{
|
||||
let line = line.start()..usize::min(end_attr, line.end());
|
||||
parser.parse(&self.input.src[line]);
|
||||
}
|
||||
parser.finish()
|
||||
};
|
||||
|
||||
for _ in 0..line_next {
|
||||
let l = self.input.ahead.pop_front().unwrap();
|
||||
self.input.set_current_line(l);
|
||||
}
|
||||
self.input.span = Span::new(start_attr, end_attr);
|
||||
self.input.lexer = lex::Lexer::new(&self.input.src[end_attr..line_end]);
|
||||
|
||||
if !attrs.is_empty() {
|
||||
let attr_event = Event {
|
||||
kind: EventKind::Attributes {
|
||||
container: matches!(elem_ty, AttributesElementType::Container { .. }),
|
||||
attrs,
|
||||
},
|
||||
span: self.input.span,
|
||||
};
|
||||
match elem_ty {
|
||||
AttributesElementType::Container { e_placeholder } => {
|
||||
self.events[e_placeholder] = attr_event;
|
||||
if matches!(self.events[e_placeholder + 1].kind, EventKind::Str) {
|
||||
self.events[e_placeholder + 1].kind = EventKind::Enter(Span);
|
||||
let last = self.events.len() - 1;
|
||||
self.events[last].kind = EventKind::Exit(Span);
|
||||
}
|
||||
}
|
||||
AttributesElementType::Word => {
|
||||
self.events.push_back(attr_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Continue)
|
||||
}
|
||||
|
||||
fn parse_autolink(&mut self, first: &lex::Token) -> Option<ControlFlow> {
|
||||
|
@ -543,7 +625,7 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
|
||||
self.openers.drain(o..);
|
||||
let mut closed = match DelimEventKind::from(opener) {
|
||||
let closed = match DelimEventKind::from(opener) {
|
||||
DelimEventKind::Container(cont) => {
|
||||
self.events[e_opener].kind = EventKind::Enter(cont);
|
||||
self.push(EventKind::Exit(cont))
|
||||
|
@ -568,8 +650,9 @@ impl<'s> Parser<'s> {
|
|||
self.input.reset_span();
|
||||
self.input.eat(); // [ or (
|
||||
return self.push(EventKind::Str);
|
||||
};
|
||||
None
|
||||
} else {
|
||||
self.push(EventKind::Str) // ]
|
||||
}
|
||||
}
|
||||
DelimEventKind::Link {
|
||||
event_span,
|
||||
|
@ -658,23 +741,18 @@ 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 { container: true },
|
||||
span,
|
||||
};
|
||||
}
|
||||
|
||||
if closed.is_none() {
|
||||
self.events[e_opener].kind = EventKind::Enter(Container::Span);
|
||||
closed = self.push(EventKind::Exit(Container::Span));
|
||||
}
|
||||
|
||||
self.input.span = span;
|
||||
if self.input.peek().map_or(false, |t| {
|
||||
matches!(t.kind, lex::Kind::Open(Delimiter::Brace))
|
||||
}) {
|
||||
self.ahead_attributes(
|
||||
AttributesElementType::Container {
|
||||
e_placeholder: e_attr,
|
||||
},
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
closed
|
||||
}
|
||||
|
||||
closed
|
||||
})
|
||||
.or_else(|| {
|
||||
let opener = Opener::from_token(first.kind)?;
|
||||
|
@ -783,7 +861,7 @@ impl<'s> Parser<'s> {
|
|||
self.push(EventKind::Atom(atom))
|
||||
}
|
||||
|
||||
fn merge_str_events(&mut self, span_str: Span) -> Event {
|
||||
fn merge_str_events(&mut self, span_str: Span) -> Event<'s> {
|
||||
let mut span = span_str;
|
||||
let should_merge = |e: &Event, span: Span| {
|
||||
matches!(e.kind, EventKind::Str | EventKind::Placeholder)
|
||||
|
@ -796,7 +874,10 @@ impl<'s> Parser<'s> {
|
|||
|
||||
if matches!(
|
||||
self.events.front().map(|ev| &ev.kind),
|
||||
Some(EventKind::Attributes { container: false })
|
||||
Some(EventKind::Attributes {
|
||||
container: false,
|
||||
..
|
||||
})
|
||||
) {
|
||||
self.apply_word_attributes(span)
|
||||
} else {
|
||||
|
@ -807,7 +888,7 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_word_attributes(&mut self, span_str: Span) -> Event {
|
||||
fn apply_word_attributes(&mut self, span_str: Span) -> Event<'s> {
|
||||
if let Some(i) = span_str
|
||||
.of(self.input.src)
|
||||
.bytes()
|
||||
|
@ -982,12 +1063,13 @@ impl From<Opener> for DelimEventKind {
|
|||
}
|
||||
|
||||
impl<'s> Iterator for Parser<'s> {
|
||||
type Item = Event;
|
||||
type Item = Event<'s>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self.events.is_empty()
|
||||
|| !self.openers.is_empty()
|
||||
|| self.verbatim.is_some()
|
||||
|| self.attributes.is_some()
|
||||
|| self // for merge or attributes
|
||||
.events
|
||||
.back()
|
||||
|
@ -1003,6 +1085,7 @@ impl<'s> Iterator for Parser<'s> {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
More => return None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1023,7 +1106,10 @@ 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 => Some(self.merge_str_events(e.span)),
|
||||
EventKind::Placeholder | EventKind::Attributes { container: false } => self.next(),
|
||||
EventKind::Placeholder
|
||||
| EventKind::Attributes {
|
||||
container: false, ..
|
||||
} => self.next(),
|
||||
_ => Some(e),
|
||||
})
|
||||
}
|
||||
|
@ -1107,7 +1193,13 @@ mod test {
|
|||
test_parse!(
|
||||
"pre `raw`{#id} post",
|
||||
(Str, "pre "),
|
||||
(Attributes { container: true }, "{#id}"),
|
||||
(
|
||||
Attributes {
|
||||
container: true,
|
||||
attrs: [("id", "id")].into_iter().collect()
|
||||
},
|
||||
"{#id}"
|
||||
),
|
||||
(Enter(Verbatim), "`"),
|
||||
(Str, "raw"),
|
||||
(Exit(Verbatim), "`"),
|
||||
|
@ -1292,7 +1384,13 @@ mod test {
|
|||
fn span_url_attr_unclosed() {
|
||||
test_parse!(
|
||||
"[text]({.cls}",
|
||||
(Attributes { container: false }, "{.cls}"),
|
||||
(
|
||||
Attributes {
|
||||
container: false,
|
||||
attrs: [("class", "cls")].into_iter().collect(),
|
||||
},
|
||||
"{.cls}"
|
||||
),
|
||||
(Enter(Span), ""),
|
||||
(Str, "[text]("),
|
||||
(Exit(Span), ""),
|
||||
|
@ -1335,7 +1433,13 @@ mod test {
|
|||
fn span_attr() {
|
||||
test_parse!(
|
||||
"[abc]{.def}",
|
||||
(Attributes { container: true }, "{.def}"),
|
||||
(
|
||||
Attributes {
|
||||
container: true,
|
||||
attrs: [("class", "def")].into_iter().collect(),
|
||||
},
|
||||
"{.def}"
|
||||
),
|
||||
(Enter(Span), "["),
|
||||
(Str, "abc"),
|
||||
(Exit(Span), "]"),
|
||||
|
@ -1343,6 +1447,23 @@ mod test {
|
|||
test_parse!("not a [span] {#id}.", (Str, "not a [span] "), (Str, "."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_attr_cont() {
|
||||
test_parse!(
|
||||
"[x_y]{.bar_}",
|
||||
(
|
||||
Attributes {
|
||||
container: true,
|
||||
attrs: [("class", "bar_")].into_iter().collect(),
|
||||
},
|
||||
"{.bar_}"
|
||||
),
|
||||
(Enter(Span), "["),
|
||||
(Str, "x_y"),
|
||||
(Exit(Span), "]"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn autolink() {
|
||||
test_parse!(
|
||||
|
@ -1440,7 +1561,13 @@ mod test {
|
|||
fn container_attr() {
|
||||
test_parse!(
|
||||
"_abc def_{.attr}",
|
||||
(Attributes { container: true }, "{.attr}"),
|
||||
(
|
||||
Attributes {
|
||||
container: true,
|
||||
attrs: [("class", "attr")].into_iter().collect(),
|
||||
},
|
||||
"{.attr}"
|
||||
),
|
||||
(Enter(Emphasis), "_"),
|
||||
(Str, "abc def"),
|
||||
(Exit(Emphasis), "_"),
|
||||
|
@ -1468,7 +1595,13 @@ mod test {
|
|||
fn container_attr_multiple() {
|
||||
test_parse!(
|
||||
"_abc def_{.a}{.b}{.c} {.d}",
|
||||
(Attributes { container: true }, "{.a}{.b}{.c}"),
|
||||
(
|
||||
Attributes {
|
||||
container: true,
|
||||
attrs: [("class", "a b c")].into_iter().collect(),
|
||||
},
|
||||
"{.a}{.b}{.c}"
|
||||
),
|
||||
(Enter(Emphasis), "_"),
|
||||
(Str, "abc def"),
|
||||
(Exit(Emphasis), "_"),
|
||||
|
@ -1480,7 +1613,13 @@ mod test {
|
|||
fn attr() {
|
||||
test_parse!(
|
||||
"word{a=b}",
|
||||
(Attributes { container: false }, "{a=b}"),
|
||||
(
|
||||
Attributes {
|
||||
container: false,
|
||||
attrs: [("a", "b")].into_iter().collect()
|
||||
},
|
||||
"{a=b}"
|
||||
),
|
||||
(Enter(Span), ""),
|
||||
(Str, "word"),
|
||||
(Exit(Span), ""),
|
||||
|
@ -1488,7 +1627,13 @@ mod test {
|
|||
test_parse!(
|
||||
"some word{.a}{.b} with attrs",
|
||||
(Str, "some "),
|
||||
(Attributes { container: false }, "{.a}{.b}"),
|
||||
(
|
||||
Attributes {
|
||||
container: false,
|
||||
attrs: [("class", "a b")].into_iter().collect(),
|
||||
},
|
||||
"{.a}{.b}"
|
||||
),
|
||||
(Enter(Span), ""),
|
||||
(Str, "word"),
|
||||
(Exit(Span), ""),
|
||||
|
@ -1501,6 +1646,7 @@ mod test {
|
|||
test_parse!("word {%comment%}", (Str, "word "));
|
||||
test_parse!("word {%comment%} word", (Str, "word "), (Str, " word"));
|
||||
test_parse!("word {a=b}", (Str, "word "));
|
||||
test_parse!("word {.d}", (Str, "word "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
101
src/lib.rs
101
src/lib.rs
|
@ -793,23 +793,15 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
|
||||
fn inline(&mut self) -> Option<Event<'s>> {
|
||||
let mut inline = self.inline_parser.next();
|
||||
let next = self.inline_parser.next()?;
|
||||
|
||||
inline.as_ref()?;
|
||||
|
||||
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 {
|
||||
first_is_attr = true;
|
||||
attr::parse(inl.span.of(self.src))
|
||||
} else {
|
||||
Attributes::new()
|
||||
}
|
||||
});
|
||||
|
||||
if first_is_attr {
|
||||
inline = self.inline_parser.next();
|
||||
}
|
||||
let (inline, mut attributes) = match next {
|
||||
inline::Event {
|
||||
kind: inline::EventKind::Attributes { attrs, .. },
|
||||
..
|
||||
} => (self.inline_parser.next(), attrs),
|
||||
inline => (Some(inline), Attributes::new()),
|
||||
};
|
||||
|
||||
inline.map(|inline| {
|
||||
let enter = matches!(inline.kind, inline::EventKind::Enter(_));
|
||||
|
@ -1706,7 +1698,6 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore = "broken"]
|
||||
#[test]
|
||||
fn attr_inline_consecutive() {
|
||||
test_parse!(
|
||||
|
@ -1733,7 +1724,6 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore = "broken"]
|
||||
#[test]
|
||||
fn attr_inline_consecutive_invalid() {
|
||||
test_parse!(
|
||||
|
@ -1776,7 +1766,6 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore = "multiline attributes broken"]
|
||||
#[test]
|
||||
fn attr_inline_multiline() {
|
||||
test_parse!(
|
||||
|
@ -1792,6 +1781,80 @@ mod test {
|
|||
End(Paragraph),
|
||||
End(Blockquote),
|
||||
);
|
||||
test_parse!(
|
||||
concat!(
|
||||
"> a{\n", //
|
||||
"> %%\n", //
|
||||
"> a=a}\n", //
|
||||
),
|
||||
Start(Blockquote, Attributes::new()),
|
||||
Start(Paragraph, Attributes::new()),
|
||||
Start(Span, [("a", "a")].into_iter().collect()),
|
||||
Str("a".into()),
|
||||
End(Span),
|
||||
End(Paragraph),
|
||||
End(Blockquote),
|
||||
);
|
||||
test_parse!(
|
||||
concat!(
|
||||
"> a{a=\"a\n", //
|
||||
"> b\n", //
|
||||
"> c\"}\n", //
|
||||
),
|
||||
Start(Blockquote, Attributes::new()),
|
||||
Start(Paragraph, Attributes::new()),
|
||||
Start(Span, [("a", "a b c")].into_iter().collect()),
|
||||
Str("a".into()),
|
||||
End(Span),
|
||||
End(Paragraph),
|
||||
End(Blockquote),
|
||||
);
|
||||
test_parse!(
|
||||
concat!(
|
||||
"> a{a=\"\n", //
|
||||
"> b\"}\n", //
|
||||
),
|
||||
Start(Blockquote, Attributes::new()),
|
||||
Start(Paragraph, Attributes::new()),
|
||||
Start(Span, [("a", "b")].into_iter().collect()),
|
||||
Str("a".into()),
|
||||
End(Span),
|
||||
End(Paragraph),
|
||||
End(Blockquote),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr_inline_multiline_unclosed() {
|
||||
test_parse!(
|
||||
concat!(
|
||||
"a{\n", //
|
||||
" b\n", //
|
||||
),
|
||||
Start(Paragraph, Attributes::new()),
|
||||
Str("a{".into()),
|
||||
Softbreak,
|
||||
Str("b".into()),
|
||||
End(Paragraph),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr_inline_multiline_invalid() {
|
||||
test_parse!(
|
||||
concat!(
|
||||
"a{a=b\n", //
|
||||
" b\n", //
|
||||
"}", //
|
||||
),
|
||||
Start(Paragraph, Attributes::new()),
|
||||
Str("a{a=b".into()),
|
||||
Softbreak,
|
||||
Str("b".into()),
|
||||
Softbreak,
|
||||
Str("}".into()),
|
||||
End(Paragraph),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
38d85f9:multi-line block attributes
|
||||
6c14561:multi-line block attributes
|
||||
613a9d6:attribute container precedence
|
||||
f4f22fc:attribute key class order
|
||||
ae6fc15:bugged left/right quote
|
||||
168469a:bugged left/right quote
|
||||
|
|
Loading…
Reference in a new issue