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
				
			
		
							
								
								
									
										158
									
								
								src/inline.rs
									
										
									
									
									
								
							
							
						
						
									
										158
									
								
								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();
 | 
					 | 
				
			||||||
                    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]"));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue