inline: impl links w/o lookahead
needed to get rid of DiscontinuousChars
This commit is contained in:
parent
66d821f03e
commit
8169feb1f6
3 changed files with 106 additions and 60 deletions
162
src/inline.rs
162
src/inline.rs
|
@ -473,10 +473,13 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
.rposition(|(o, _)| o.closed_by(first.kind))
|
.rposition(|(o, _)| o.closed_by(first.kind))
|
||||||
.and_then(|o| {
|
.and_then(|o| {
|
||||||
let (opener, e) = self.openers[o];
|
let (opener, e) = self.openers[o];
|
||||||
let e_attr = e;
|
let (e_attr, e_opener) = if let Opener::Link { event_span, .. } = opener {
|
||||||
let e_opener = e + 1;
|
(event_span - 1, e)
|
||||||
|
} else {
|
||||||
|
(e, e + 1)
|
||||||
|
};
|
||||||
|
|
||||||
if e_opener == self.events.len() - 1 {
|
if e_opener == self.events.len() - 1 && !matches!(opener, Opener::Link { .. }) {
|
||||||
// empty container
|
// empty container
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -487,7 +490,6 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner_span = self.events[e_opener].span.between(self.input.span);
|
|
||||||
self.openers.drain(o..);
|
self.openers.drain(o..);
|
||||||
let mut closed = match DelimEventKind::from(opener) {
|
let mut closed = match DelimEventKind::from(opener) {
|
||||||
DelimEventKind::Container(cont) => {
|
DelimEventKind::Container(cont) => {
|
||||||
|
@ -498,21 +500,56 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
self.events[e_opener].kind = EventKind::Atom(Quote { ty, left: true });
|
self.events[e_opener].kind = EventKind::Atom(Quote { ty, left: true });
|
||||||
self.push(EventKind::Atom(Quote { ty, left: false }))
|
self.push(EventKind::Atom(Quote { ty, left: false }))
|
||||||
}
|
}
|
||||||
DelimEventKind::Span(ty) => self.post_span(ty, e_opener),
|
DelimEventKind::Span(ty) => {
|
||||||
};
|
if let Some(lex::Kind::Open(d @ (Delimiter::Bracket | Delimiter::Paren))) =
|
||||||
|
self.input.peek().map(|t| t.kind)
|
||||||
if closed.is_some() {
|
{
|
||||||
let event_closer = &mut self.events.back_mut().unwrap();
|
self.push(EventKind::Str); // ]
|
||||||
if event_closer.span.is_empty()
|
self.openers.push((
|
||||||
&& matches!(
|
Opener::Link {
|
||||||
event_closer.kind,
|
event_span: e_opener,
|
||||||
EventKind::Exit(ReferenceLink | ReferenceImage)
|
image: matches!(ty, SpanType::Image),
|
||||||
)
|
inline: matches!(d, Delimiter::Paren),
|
||||||
{
|
},
|
||||||
event_closer.span = inner_span;
|
self.events.len(),
|
||||||
self.events[e_opener].span = inner_span;
|
));
|
||||||
|
self.input.reset_span();
|
||||||
|
self.input.eat(); // [ or (
|
||||||
|
return self.push(EventKind::Str);
|
||||||
|
};
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
DelimEventKind::Link {
|
||||||
|
event_span,
|
||||||
|
inline,
|
||||||
|
image,
|
||||||
|
} => {
|
||||||
|
let span_spec = self.events[e_opener].span.between(self.input.span);
|
||||||
|
let span_spec = if !inline && span_spec.is_empty() {
|
||||||
|
self.events[event_span]
|
||||||
|
.span
|
||||||
|
.between(self.events[e_opener - 1].span)
|
||||||
|
} else {
|
||||||
|
span_spec
|
||||||
|
};
|
||||||
|
let container = match (image, inline) {
|
||||||
|
(false, false) => ReferenceLink,
|
||||||
|
(false, true) => InlineLink,
|
||||||
|
(true, false) => ReferenceImage,
|
||||||
|
(true, true) => InlineImage,
|
||||||
|
};
|
||||||
|
self.events[event_span] = Event {
|
||||||
|
kind: EventKind::Enter(container),
|
||||||
|
span: span_spec,
|
||||||
|
};
|
||||||
|
self.events[e_opener - 1] = Event {
|
||||||
|
kind: EventKind::Exit(container),
|
||||||
|
span: span_spec,
|
||||||
|
};
|
||||||
|
self.events.drain(e_opener..);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((non_empty, span)) = self.input.ahead_attributes() {
|
if let Some((non_empty, span)) = self.input.ahead_attributes() {
|
||||||
if non_empty {
|
if non_empty {
|
||||||
|
@ -571,47 +608,6 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_span(&mut self, ty: SpanType, opener_event: usize) -> Option<()> {
|
|
||||||
let mut ahead = self.input.lexer.chars();
|
|
||||||
let kind = match ahead.next() {
|
|
||||||
Some(opener @ ('[' | '(')) => {
|
|
||||||
let img = ty == SpanType::Image;
|
|
||||||
let (closer, kind) = match opener {
|
|
||||||
'[' => (']', if img { ReferenceImage } else { ReferenceLink }),
|
|
||||||
'(' => (')', if img { InlineImage } else { InlineLink }),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let mut end = false;
|
|
||||||
let len = (&mut ahead)
|
|
||||||
.take_while(|c| {
|
|
||||||
if *c == opener {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if *c == closer {
|
|
||||||
end = true;
|
|
||||||
};
|
|
||||||
!end
|
|
||||||
})
|
|
||||||
.map(char::len_utf8)
|
|
||||||
.sum();
|
|
||||||
if end {
|
|
||||||
self.input.span = self.input.span.after(len).translate(1);
|
|
||||||
Some(kind)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}?;
|
|
||||||
|
|
||||||
self.input.lexer = lex::Lexer::new(ahead);
|
|
||||||
self.events[opener_event].kind = EventKind::Enter(kind);
|
|
||||||
self.events[opener_event].span = self.input.span;
|
|
||||||
self.push(EventKind::Exit(kind));
|
|
||||||
self.input.span = self.input.span.translate(1);
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_atom(&mut self, first: &lex::Token) -> Option<()> {
|
fn parse_atom(&mut self, first: &lex::Token) -> Option<()> {
|
||||||
let atom = match first.kind {
|
let atom = match first.kind {
|
||||||
lex::Kind::Newline => Softbreak,
|
lex::Kind::Newline => Softbreak,
|
||||||
|
@ -695,6 +691,11 @@ enum Opener {
|
||||||
Insert,
|
Insert,
|
||||||
SingleQuoted,
|
SingleQuoted,
|
||||||
DoubleQuoted,
|
DoubleQuoted,
|
||||||
|
Link {
|
||||||
|
event_span: usize,
|
||||||
|
image: bool,
|
||||||
|
inline: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opener {
|
impl Opener {
|
||||||
|
@ -750,6 +751,8 @@ impl Opener {
|
||||||
kind,
|
kind,
|
||||||
lex::Kind::Sym(Symbol::Quote2) | lex::Kind::Close(Delimiter::BraceQuote2)
|
lex::Kind::Sym(Symbol::Quote2) | lex::Kind::Close(Delimiter::BraceQuote2)
|
||||||
),
|
),
|
||||||
|
Link { inline: false, .. } => matches!(kind, lex::Kind::Close(Delimiter::Bracket)),
|
||||||
|
Link { inline: true, .. } => matches!(kind, lex::Kind::Close(Delimiter::Paren)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,6 +773,11 @@ enum DelimEventKind {
|
||||||
Container(Container),
|
Container(Container),
|
||||||
Span(SpanType),
|
Span(SpanType),
|
||||||
Quote(QuoteType),
|
Quote(QuoteType),
|
||||||
|
Link {
|
||||||
|
event_span: usize,
|
||||||
|
image: bool,
|
||||||
|
inline: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Opener> for DelimEventKind {
|
impl From<Opener> for DelimEventKind {
|
||||||
|
@ -785,6 +793,15 @@ impl From<Opener> for DelimEventKind {
|
||||||
Opener::Insert => Self::Container(Insert),
|
Opener::Insert => Self::Container(Insert),
|
||||||
Opener::SingleQuoted => Self::Quote(QuoteType::Single),
|
Opener::SingleQuoted => Self::Quote(QuoteType::Single),
|
||||||
Opener::DoubleQuoted => Self::Quote(QuoteType::Double),
|
Opener::DoubleQuoted => Self::Quote(QuoteType::Double),
|
||||||
|
Opener::Link {
|
||||||
|
event_span,
|
||||||
|
image,
|
||||||
|
inline,
|
||||||
|
} => Self::Link {
|
||||||
|
event_span,
|
||||||
|
image,
|
||||||
|
inline,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1108,6 +1125,28 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn span_url_attr_unclosed() {
|
||||||
|
test_parse!(
|
||||||
|
"[text]({.cls}",
|
||||||
|
(Attributes, "{.cls}"),
|
||||||
|
(Enter(Span), ""),
|
||||||
|
(Str, "[text]("),
|
||||||
|
(Exit(Span), ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore = "broken"]
|
||||||
|
#[test]
|
||||||
|
fn span_url_attr_closed() {
|
||||||
|
test_parse!(
|
||||||
|
"[text]({.cls})",
|
||||||
|
(Enter(InlineLink), "{.cls}"),
|
||||||
|
(Str, "text"),
|
||||||
|
(Exit(InlineLink), "{.cls}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn span_url_empty() {
|
fn span_url_empty() {
|
||||||
test_parse!(
|
test_parse!(
|
||||||
|
@ -1120,6 +1159,11 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn span_url_unclosed() {
|
||||||
|
test_parse!("[text](url", (Str, "[text](url"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn span() {
|
fn span() {
|
||||||
test_parse!("[abc]", (Str, "[abc]"));
|
test_parse!("[abc]", (Str, "[abc]"));
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub enum Delimiter {
|
||||||
Bracket,
|
Bracket,
|
||||||
BraceQuote1,
|
BraceQuote1,
|
||||||
BraceQuote2,
|
BraceQuote2,
|
||||||
|
Paren,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -186,6 +187,8 @@ impl<I: Iterator<Item = char> + Clone> Lexer<I> {
|
||||||
|
|
||||||
'[' => Open(Bracket),
|
'[' => Open(Bracket),
|
||||||
']' => Close(Bracket),
|
']' => Close(Bracket),
|
||||||
|
'(' => Open(Paren),
|
||||||
|
')' => Close(Paren),
|
||||||
'{' => {
|
'{' => {
|
||||||
let explicit = match self.peek_char() {
|
let explicit = match self.peek_char() {
|
||||||
Some('*') => Some(Open(BraceAsterisk)),
|
Some('*') => Some(Open(BraceAsterisk)),
|
||||||
|
|
|
@ -11,7 +11,6 @@ e1f5b5e:untrimmed whitespace before linebreak
|
||||||
8423412:heading id conflict with existing id
|
8423412:heading id conflict with existing id
|
||||||
00a46ed:clear inline formatting from link tags
|
00a46ed:clear inline formatting from link tags
|
||||||
c0a3dec:escape in url
|
c0a3dec:escape in url
|
||||||
e66af00:url container precedence
|
|
||||||
61876cf:roman alpha ambiguity
|
61876cf:roman alpha ambiguity
|
||||||
f31b357:roman alpha ambiguity
|
f31b357:roman alpha ambiguity
|
||||||
642d380:table end in verbatim inline
|
642d380:table end in verbatim inline
|
||||||
|
|
Loading…
Reference in a new issue