inline: impl links w/o lookahead

needed to get rid of DiscontinuousChars
This commit is contained in:
Noah Hellman 2023-02-19 10:56:49 +01:00
parent 66d821f03e
commit 8169feb1f6
3 changed files with 106 additions and 60 deletions

View file

@ -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();
if event_closer.span.is_empty()
&& matches!(
event_closer.kind,
EventKind::Exit(ReferenceLink | ReferenceImage)
)
{ {
event_closer.span = inner_span; self.push(EventKind::Str); // ]
self.events[e_opener].span = inner_span; self.openers.push((
Opener::Link {
event_span: e_opener,
image: matches!(ty, SpanType::Image),
inline: matches!(d, Delimiter::Paren),
},
self.events.len(),
));
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]"));

View file

@ -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)),

View file

@ -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