inline: take str per line instead of full inline iter
gets rid of DiscontinousChars which is large and requires cloning on peek resolves #4
This commit is contained in:
		
					parent
					
						
							
								8169feb1f6
							
						
					
				
			
			
				commit
				
					
						3a1a3996e9
					
				
			
		
					 7 changed files with 145 additions and 349 deletions
				
			
		
							
								
								
									
										22
									
								
								src/attr.rs
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								src/attr.rs
									
										
									
									
									
								
							| 
						 | 
					@ -1,14 +1,12 @@
 | 
				
			||||||
use crate::CowStr;
 | 
					use crate::CowStr;
 | 
				
			||||||
use crate::DiscontinuousString;
 | 
					 | 
				
			||||||
use crate::Span;
 | 
					use crate::Span;
 | 
				
			||||||
use std::borrow::Cow;
 | 
					 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use State::*;
 | 
					use State::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) fn parse<'s, S: DiscontinuousString<'s>>(chars: S) -> Attributes<'s> {
 | 
					pub(crate) fn parse(src: &str) -> Attributes {
 | 
				
			||||||
    let mut a = Attributes::new();
 | 
					    let mut a = Attributes::new();
 | 
				
			||||||
    a.parse(chars);
 | 
					    a.parse(src);
 | 
				
			||||||
    a
 | 
					    a
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,20 +111,12 @@ impl<'s> Attributes<'s> {
 | 
				
			||||||
        Self(self.0.take())
 | 
					        Self(self.0.take())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn parse<S: DiscontinuousString<'s>>(&mut self, input: S) -> bool {
 | 
					    pub(crate) fn parse(&mut self, input: &'s str) -> bool {
 | 
				
			||||||
        #[inline]
 | 
					 | 
				
			||||||
        fn borrow(cow: CowStr) -> &str {
 | 
					 | 
				
			||||||
            match cow {
 | 
					 | 
				
			||||||
                Cow::Owned(_) => panic!(),
 | 
					 | 
				
			||||||
                Cow::Borrowed(s) => s,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for elem in Parser::new(input.chars()) {
 | 
					        for elem in Parser::new(input.chars()) {
 | 
				
			||||||
            match elem {
 | 
					            match elem {
 | 
				
			||||||
                Element::Class(c) => self.insert("class", input.src(c).into()),
 | 
					                Element::Class(c) => self.insert("class", c.of(input).into()),
 | 
				
			||||||
                Element::Identifier(i) => self.insert("id", input.src(i).into()),
 | 
					                Element::Identifier(i) => self.insert("id", i.of(input).into()),
 | 
				
			||||||
                Element::Attribute(a, v) => self.insert(borrow(input.src(a)), input.src(v).into()),
 | 
					                Element::Attribute(a, v) => self.insert(a.of(input), v.of(input).into()),
 | 
				
			||||||
                Element::Invalid => return false,
 | 
					                Element::Invalid => return false,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -447,7 +447,7 @@ impl<'s> TreeParser<'s> {
 | 
				
			||||||
                .tree
 | 
					                .tree
 | 
				
			||||||
                .enter(Node::Container(TableRow { head: false }), row.with_len(1));
 | 
					                .enter(Node::Container(TableRow { head: false }), row.with_len(1));
 | 
				
			||||||
            let rem = row.skip(1); // |
 | 
					            let rem = row.skip(1); // |
 | 
				
			||||||
            let lex = lex::Lexer::new(rem.of(self.src).chars());
 | 
					            let lex = lex::Lexer::new(rem.of(self.src));
 | 
				
			||||||
            let mut pos = rem.start();
 | 
					            let mut pos = rem.start();
 | 
				
			||||||
            let mut cell_start = pos;
 | 
					            let mut cell_start = pos;
 | 
				
			||||||
            let mut separator_row = true;
 | 
					            let mut separator_row = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,24 +73,33 @@ pub struct Event {
 | 
				
			||||||
    pub span: Span,
 | 
					    pub span: Span,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Input<I: Iterator + Clone> {
 | 
					pub struct Input<'s> {
 | 
				
			||||||
    /// Lexer, hosting source.
 | 
					    /// Lexer, hosting source.
 | 
				
			||||||
    lexer: lex::Lexer<I>,
 | 
					    lexer: lex::Lexer<'s>,
 | 
				
			||||||
 | 
					    /// The block is complete, the final line has been provided.
 | 
				
			||||||
 | 
					    complete: bool,
 | 
				
			||||||
    /// Span of current event.
 | 
					    /// Span of current event.
 | 
				
			||||||
    span: Span,
 | 
					    span: Span,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: Iterator<Item = char> + Clone> Input<I> {
 | 
					impl<'s> Input<'s> {
 | 
				
			||||||
    fn new(chars: I) -> Self {
 | 
					    fn new() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            lexer: lex::Lexer::new(chars),
 | 
					            lexer: lex::Lexer::new(""),
 | 
				
			||||||
            span: Span::new(0, 0),
 | 
					            complete: false,
 | 
				
			||||||
 | 
					            span: Span::empty_at(0),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn reset(&mut self, chars: I) {
 | 
					    fn feed_line(&mut self, line: &'s str, offset: usize, last: bool) {
 | 
				
			||||||
        self.lexer = lex::Lexer::new(chars);
 | 
					        debug_assert!(!self.complete);
 | 
				
			||||||
        self.span = Span::new(0, 0);
 | 
					        self.lexer = lex::Lexer::new(line);
 | 
				
			||||||
 | 
					        self.complete = last;
 | 
				
			||||||
 | 
					        self.span = Span::empty_at(offset);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn reset(&mut self) {
 | 
				
			||||||
 | 
					        *self = Self::new();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn eat(&mut self) -> Option<lex::Token> {
 | 
					    fn eat(&mut self) -> Option<lex::Token> {
 | 
				
			||||||
| 
						 | 
					@ -111,12 +120,12 @@ impl<I: Iterator<Item = char> + Clone> Input<I> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn ahead_attributes(&mut self) -> Option<(bool, Span)> {
 | 
					    fn ahead_attributes(&mut self) -> Option<(bool, Span)> {
 | 
				
			||||||
        let mut span = self.span.empty_after();
 | 
					        let mut span = self.span.empty_after();
 | 
				
			||||||
        let mut ahead = self.lexer.chars();
 | 
					        let mut ahead = self.lexer.ahead().chars();
 | 
				
			||||||
        let (mut attr_len, mut has_attr) = attr::valid(&mut ahead);
 | 
					        let (mut attr_len, mut has_attr) = attr::valid(&mut ahead);
 | 
				
			||||||
        if attr_len > 0 {
 | 
					        if attr_len > 0 {
 | 
				
			||||||
            while attr_len > 0 {
 | 
					            while attr_len > 0 {
 | 
				
			||||||
                span = span.extend(attr_len);
 | 
					                span = span.extend(attr_len);
 | 
				
			||||||
                self.lexer = lex::Lexer::new(ahead.clone());
 | 
					                self.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let (l, non_empty) = attr::valid(&mut ahead);
 | 
					                let (l, non_empty) = attr::valid(&mut ahead);
 | 
				
			||||||
                has_attr |= non_empty;
 | 
					                has_attr |= non_empty;
 | 
				
			||||||
| 
						 | 
					@ -133,7 +142,7 @@ impl<I: Iterator<Item = char> + Clone> Input<I> {
 | 
				
			||||||
            self.lexer.peek().map(|t| &t.kind),
 | 
					            self.lexer.peek().map(|t| &t.kind),
 | 
				
			||||||
            Some(lex::Kind::Open(Delimiter::BraceEqual))
 | 
					            Some(lex::Kind::Open(Delimiter::BraceEqual))
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            let mut ahead = self.lexer.chars();
 | 
					            let mut ahead = self.lexer.ahead().chars();
 | 
				
			||||||
            let mut end = false;
 | 
					            let mut end = false;
 | 
				
			||||||
            let len = (&mut ahead)
 | 
					            let len = (&mut ahead)
 | 
				
			||||||
                .skip(2) // {=
 | 
					                .skip(2) // {=
 | 
				
			||||||
| 
						 | 
					@ -157,7 +166,7 @@ impl<I: Iterator<Item = char> + Clone> Input<I> {
 | 
				
			||||||
                        len: 2,
 | 
					                        len: 2,
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                self.lexer = lex::Lexer::new(ahead);
 | 
					                self.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
                self.span.after(len)
 | 
					                self.span.after(len)
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					@ -173,8 +182,8 @@ pub struct VerbatimState {
 | 
				
			||||||
    non_whitespace_last: Option<(lex::Kind, usize)>,
 | 
					    non_whitespace_last: Option<(lex::Kind, usize)>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Parser<I: Iterator + Clone> {
 | 
					pub struct Parser<'s> {
 | 
				
			||||||
    input: Input<I>,
 | 
					    input: Input<'s>,
 | 
				
			||||||
    /// Stack with kind and index of _potential_ openers for containers.
 | 
					    /// Stack with kind and index of _potential_ openers for containers.
 | 
				
			||||||
    openers: Vec<(Opener, usize)>,
 | 
					    openers: Vec<(Opener, usize)>,
 | 
				
			||||||
    /// Buffer queue for next events. Events are buffered until no modifications due to future
 | 
					    /// Buffer queue for next events. Events are buffered until no modifications due to future
 | 
				
			||||||
| 
						 | 
					@ -184,18 +193,23 @@ pub struct Parser<I: Iterator + Clone> {
 | 
				
			||||||
    verbatim: Option<VerbatimState>,
 | 
					    verbatim: Option<VerbatimState>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
					impl<'s> Parser<'s> {
 | 
				
			||||||
    pub fn new(chars: I) -> Self {
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            input: Input::new(chars),
 | 
					            input: Input::new(),
 | 
				
			||||||
            openers: Vec::new(),
 | 
					            openers: Vec::new(),
 | 
				
			||||||
            events: std::collections::VecDeque::new(),
 | 
					            events: std::collections::VecDeque::new(),
 | 
				
			||||||
            verbatim: None,
 | 
					            verbatim: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn reset(&mut self, chars: I) {
 | 
					    pub fn feed_line(&mut self, line: &'s str, offset: usize, last: bool) {
 | 
				
			||||||
        self.input.reset(chars);
 | 
					        self.input.feed_line(line, offset, last);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn reset(&mut self) {
 | 
				
			||||||
 | 
					        debug_assert!(self.events.is_empty());
 | 
				
			||||||
 | 
					        self.input.reset();
 | 
				
			||||||
        self.openers.clear();
 | 
					        self.openers.clear();
 | 
				
			||||||
        debug_assert!(self.events.is_empty());
 | 
					        debug_assert!(self.events.is_empty());
 | 
				
			||||||
        debug_assert!(self.verbatim.is_none());
 | 
					        debug_assert!(self.verbatim.is_none());
 | 
				
			||||||
| 
						 | 
					@ -321,13 +335,13 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn parse_attributes(&mut self, first: &lex::Token) -> Option<()> {
 | 
					    fn parse_attributes(&mut self, first: &lex::Token) -> Option<()> {
 | 
				
			||||||
        if first.kind == lex::Kind::Open(Delimiter::Brace) {
 | 
					        if first.kind == lex::Kind::Open(Delimiter::Brace) {
 | 
				
			||||||
            let mut ahead = self.input.lexer.chars();
 | 
					            let mut ahead = self.input.lexer.ahead().chars();
 | 
				
			||||||
            let (mut attr_len, mut has_attr) = attr::valid(std::iter::once('{').chain(&mut ahead));
 | 
					            let (mut attr_len, mut has_attr) = attr::valid(std::iter::once('{').chain(&mut ahead));
 | 
				
			||||||
            attr_len = attr_len.saturating_sub(1); // rm {
 | 
					            attr_len = attr_len.saturating_sub(1); // rm {
 | 
				
			||||||
            if attr_len > 0 {
 | 
					            if attr_len > 0 {
 | 
				
			||||||
                while attr_len > 0 {
 | 
					                while attr_len > 0 {
 | 
				
			||||||
                    self.input.span = self.input.span.extend(attr_len);
 | 
					                    self.input.span = self.input.span.extend(attr_len);
 | 
				
			||||||
                    self.input.lexer = lex::Lexer::new(ahead.clone());
 | 
					                    self.input.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let (l, non_empty) = attr::valid(&mut ahead);
 | 
					                    let (l, non_empty) = attr::valid(&mut ahead);
 | 
				
			||||||
                    attr_len = l;
 | 
					                    attr_len = l;
 | 
				
			||||||
| 
						 | 
					@ -367,7 +381,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn parse_autolink(&mut self, first: &lex::Token) -> Option<()> {
 | 
					    fn parse_autolink(&mut self, first: &lex::Token) -> Option<()> {
 | 
				
			||||||
        if first.kind == lex::Kind::Sym(Symbol::Lt) {
 | 
					        if first.kind == lex::Kind::Sym(Symbol::Lt) {
 | 
				
			||||||
            let mut ahead = self.input.lexer.chars();
 | 
					            let mut ahead = self.input.lexer.ahead().chars();
 | 
				
			||||||
            let mut end = false;
 | 
					            let mut end = false;
 | 
				
			||||||
            let mut is_url = false;
 | 
					            let mut is_url = false;
 | 
				
			||||||
            let len = (&mut ahead)
 | 
					            let len = (&mut ahead)
 | 
				
			||||||
| 
						 | 
					@ -386,7 +400,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
                .map(char::len_utf8)
 | 
					                .map(char::len_utf8)
 | 
				
			||||||
                .sum();
 | 
					                .sum();
 | 
				
			||||||
            if end && is_url {
 | 
					            if end && is_url {
 | 
				
			||||||
                self.input.lexer = lex::Lexer::new(ahead);
 | 
					                self.input.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
                self.input.span = self.input.span.after(len);
 | 
					                self.input.span = self.input.span.after(len);
 | 
				
			||||||
                self.push(EventKind::Enter(Autolink));
 | 
					                self.push(EventKind::Enter(Autolink));
 | 
				
			||||||
                self.push(EventKind::Str);
 | 
					                self.push(EventKind::Str);
 | 
				
			||||||
| 
						 | 
					@ -399,7 +413,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn parse_symbol(&mut self, first: &lex::Token) -> Option<()> {
 | 
					    fn parse_symbol(&mut self, first: &lex::Token) -> Option<()> {
 | 
				
			||||||
        if first.kind == lex::Kind::Sym(Symbol::Colon) {
 | 
					        if first.kind == lex::Kind::Sym(Symbol::Colon) {
 | 
				
			||||||
            let mut ahead = self.input.lexer.chars();
 | 
					            let mut ahead = self.input.lexer.ahead().chars();
 | 
				
			||||||
            let mut end = false;
 | 
					            let mut end = false;
 | 
				
			||||||
            let mut valid = true;
 | 
					            let mut valid = true;
 | 
				
			||||||
            let len = (&mut ahead)
 | 
					            let len = (&mut ahead)
 | 
				
			||||||
| 
						 | 
					@ -414,7 +428,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
                .map(char::len_utf8)
 | 
					                .map(char::len_utf8)
 | 
				
			||||||
                .sum();
 | 
					                .sum();
 | 
				
			||||||
            if end && valid {
 | 
					            if end && valid {
 | 
				
			||||||
                self.input.lexer = lex::Lexer::new(ahead);
 | 
					                self.input.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
                self.input.span = self.input.span.after(len);
 | 
					                self.input.span = self.input.span.after(len);
 | 
				
			||||||
                self.push(EventKind::Atom(Symbol));
 | 
					                self.push(EventKind::Atom(Symbol));
 | 
				
			||||||
                self.input.span = self.input.span.after(1);
 | 
					                self.input.span = self.input.span.after(1);
 | 
				
			||||||
| 
						 | 
					@ -442,7 +456,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
                    len: 1,
 | 
					                    len: 1,
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            let mut ahead = self.input.lexer.chars();
 | 
					            let mut ahead = self.input.lexer.ahead().chars();
 | 
				
			||||||
            let mut end = false;
 | 
					            let mut end = false;
 | 
				
			||||||
            let len = (&mut ahead)
 | 
					            let len = (&mut ahead)
 | 
				
			||||||
                .take_while(|c| {
 | 
					                .take_while(|c| {
 | 
				
			||||||
| 
						 | 
					@ -457,7 +471,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
 | 
				
			||||||
                .map(char::len_utf8)
 | 
					                .map(char::len_utf8)
 | 
				
			||||||
                .sum();
 | 
					                .sum();
 | 
				
			||||||
            if end {
 | 
					            if end {
 | 
				
			||||||
                self.input.lexer = lex::Lexer::new(ahead);
 | 
					                self.input.lexer = lex::Lexer::new(ahead.as_str());
 | 
				
			||||||
                self.input.span = self.input.span.after(len);
 | 
					                self.input.span = self.input.span.after(len);
 | 
				
			||||||
                self.push(EventKind::Atom(FootnoteReference));
 | 
					                self.push(EventKind::Atom(FootnoteReference));
 | 
				
			||||||
                self.input.span = self.input.span.after(1);
 | 
					                self.input.span = self.input.span.after(1);
 | 
				
			||||||
| 
						 | 
					@ -806,7 +820,7 @@ impl From<Opener> for DelimEventKind {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: Iterator<Item = char> + Clone> Iterator for Parser<I> {
 | 
					impl<'s> Iterator for Parser<'s> {
 | 
				
			||||||
    type Item = Event;
 | 
					    type Item = Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
					    fn next(&mut self) -> Option<Self::Item> {
 | 
				
			||||||
| 
						 | 
					@ -821,7 +835,11 @@ impl<I: Iterator<Item = char> + Clone> Iterator for Parser<I> {
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if self.parse_event().is_none() {
 | 
					            if self.parse_event().is_none() {
 | 
				
			||||||
                break;
 | 
					                if self.input.complete {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    return None;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -878,7 +896,8 @@ mod test {
 | 
				
			||||||
    macro_rules! test_parse {
 | 
					    macro_rules! test_parse {
 | 
				
			||||||
        ($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => {
 | 
					        ($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => {
 | 
				
			||||||
            #[allow(unused)]
 | 
					            #[allow(unused)]
 | 
				
			||||||
            let mut p = super::Parser::new($src.chars());
 | 
					            let mut p = super::Parser::new();
 | 
				
			||||||
 | 
					            p.feed_line($src, 0, true);
 | 
				
			||||||
            let actual = p.map(|ev| (ev.kind, ev.span.of($src))).collect::<Vec<_>>();
 | 
					            let actual = p.map(|ev| (ev.kind, ev.span.of($src))).collect::<Vec<_>>();
 | 
				
			||||||
            let expected = &[$($($token),*,)?];
 | 
					            let expected = &[$($($token),*,)?];
 | 
				
			||||||
            assert_eq!(actual, expected, "\n\n{}\n\n", $src);
 | 
					            assert_eq!(actual, expected, "\n\n{}\n\n", $src);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/lex.rs
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								src/lex.rs
									
										
									
									
									
								
							| 
						 | 
					@ -72,9 +72,9 @@ impl Sequence {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
pub(crate) struct Lexer<I: Iterator + Clone> {
 | 
					pub(crate) struct Lexer<'s> {
 | 
				
			||||||
    chars: I,
 | 
					    src: &'s str,
 | 
				
			||||||
    chars_non_peeked: I,
 | 
					    chars: std::str::Chars<'s>,
 | 
				
			||||||
    /// Next character should be escaped.
 | 
					    /// Next character should be escaped.
 | 
				
			||||||
    escape: bool,
 | 
					    escape: bool,
 | 
				
			||||||
    /// Token to be peeked or next'ed.
 | 
					    /// Token to be peeked or next'ed.
 | 
				
			||||||
| 
						 | 
					@ -83,11 +83,11 @@ pub(crate) struct Lexer<I: Iterator + Clone> {
 | 
				
			||||||
    len: usize,
 | 
					    len: usize,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: Iterator<Item = char> + Clone> Lexer<I> {
 | 
					impl<'s> Lexer<'s> {
 | 
				
			||||||
    pub fn new(chars: I) -> Lexer<I> {
 | 
					    pub fn new(src: &'s str) -> Self {
 | 
				
			||||||
        Lexer {
 | 
					        Lexer {
 | 
				
			||||||
            chars: chars.clone(),
 | 
					            src,
 | 
				
			||||||
            chars_non_peeked: chars,
 | 
					            chars: src.chars(),
 | 
				
			||||||
            escape: false,
 | 
					            escape: false,
 | 
				
			||||||
            next: None,
 | 
					            next: None,
 | 
				
			||||||
            len: 0,
 | 
					            len: 0,
 | 
				
			||||||
| 
						 | 
					@ -103,13 +103,14 @@ impl<I: Iterator<Item = char> + Clone> Lexer<I> {
 | 
				
			||||||
        self.next.as_ref()
 | 
					        self.next.as_ref()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn chars(&self) -> I {
 | 
					    pub fn ahead(&self) -> &'s str {
 | 
				
			||||||
        self.chars_non_peeked.clone()
 | 
					        let pos =
 | 
				
			||||||
 | 
					            self.src.len() - self.chars.as_str().len() - self.next.as_ref().map_or(0, |t| t.len);
 | 
				
			||||||
 | 
					        &self.src[pos..]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn next_token(&mut self) -> Option<Token> {
 | 
					    fn next_token(&mut self) -> Option<Token> {
 | 
				
			||||||
        let mut current = self.token();
 | 
					        let mut current = self.token();
 | 
				
			||||||
        self.chars_non_peeked = self.chars.clone();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // concatenate text tokens
 | 
					        // concatenate text tokens
 | 
				
			||||||
        if let Some(Token { kind: Text, len }) = &mut current {
 | 
					        if let Some(Token { kind: Text, len }) = &mut current {
 | 
				
			||||||
| 
						 | 
					@ -148,7 +149,6 @@ impl<I: Iterator<Item = char> + Clone> Lexer<I> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn token(&mut self) -> Option<Token> {
 | 
					    fn token(&mut self) -> Option<Token> {
 | 
				
			||||||
        self.chars_non_peeked = self.chars.clone();
 | 
					 | 
				
			||||||
        self.len = 0;
 | 
					        self.len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let first = self.eat_char()?;
 | 
					        let first = self.eat_char()?;
 | 
				
			||||||
| 
						 | 
					@ -283,17 +283,11 @@ impl<I: Iterator<Item = char> + Clone> Lexer<I> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: Iterator<Item = char> + Clone> Iterator for Lexer<I> {
 | 
					impl<'s> Iterator for Lexer<'s> {
 | 
				
			||||||
    type Item = Token;
 | 
					    type Item = Token;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
					    fn next(&mut self) -> Option<Self::Item> {
 | 
				
			||||||
        self.next
 | 
					        self.next.take().or_else(|| self.next_token())
 | 
				
			||||||
            .take()
 | 
					 | 
				
			||||||
            .map(|x| {
 | 
					 | 
				
			||||||
                self.chars_non_peeked = self.chars.clone();
 | 
					 | 
				
			||||||
                x
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .or_else(|| self.next_token())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -307,7 +301,7 @@ mod test {
 | 
				
			||||||
    macro_rules! test_lex {
 | 
					    macro_rules! test_lex {
 | 
				
			||||||
        ($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => {
 | 
					        ($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => {
 | 
				
			||||||
            #[allow(unused)]
 | 
					            #[allow(unused)]
 | 
				
			||||||
            let actual = super::Lexer::new($src.chars()).collect::<Vec<_>>();
 | 
					            let actual = super::Lexer::new($src).collect::<Vec<_>>();
 | 
				
			||||||
            let expected = vec![$($($token),*,)?];
 | 
					            let expected = vec![$($($token),*,)?];
 | 
				
			||||||
            assert_eq!(actual, expected, "{}", $src);
 | 
					            assert_eq!(actual, expected, "{}", $src);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										144
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										144
									
								
								src/lib.rs
									
										
									
									
									
								
							| 
						 | 
					@ -62,7 +62,6 @@ mod lex;
 | 
				
			||||||
mod span;
 | 
					mod span;
 | 
				
			||||||
mod tree;
 | 
					mod tree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use span::DiscontinuousString;
 | 
					 | 
				
			||||||
use span::Span;
 | 
					use span::Span;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use attr::{AttributeValue, AttributeValueParts, Attributes};
 | 
					pub use attr::{AttributeValue, AttributeValueParts, Attributes};
 | 
				
			||||||
| 
						 | 
					@ -593,6 +592,9 @@ pub struct Parser<'s> {
 | 
				
			||||||
    /// Current table row is a head row.
 | 
					    /// Current table row is a head row.
 | 
				
			||||||
    table_head_row: bool,
 | 
					    table_head_row: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Currently within a verbatim code block.
 | 
				
			||||||
 | 
					    verbatim: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Footnote references in the order they were encountered, without duplicates.
 | 
					    /// Footnote references in the order they were encountered, without duplicates.
 | 
				
			||||||
    footnote_references: Vec<&'s str>,
 | 
					    footnote_references: Vec<&'s str>,
 | 
				
			||||||
    /// Cache of footnotes to emit at the end.
 | 
					    /// Cache of footnotes to emit at the end.
 | 
				
			||||||
| 
						 | 
					@ -602,10 +604,8 @@ pub struct Parser<'s> {
 | 
				
			||||||
    /// Currently within a footnote.
 | 
					    /// Currently within a footnote.
 | 
				
			||||||
    footnote_active: bool,
 | 
					    footnote_active: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Spans to the inlines in the leaf block currently being parsed.
 | 
					 | 
				
			||||||
    inlines: span::InlineSpans<'s>,
 | 
					 | 
				
			||||||
    /// Inline parser.
 | 
					    /// Inline parser.
 | 
				
			||||||
    inline_parser: inline::Parser<span::InlineCharsIter<'s>>,
 | 
					    inline_parser: inline::Parser<'s>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Heading {
 | 
					struct Heading {
 | 
				
			||||||
| 
						 | 
					@ -629,17 +629,11 @@ struct PrePass<'s> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'s> PrePass<'s> {
 | 
					impl<'s> PrePass<'s> {
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    fn new(
 | 
					    fn new(src: &'s str, mut tree: block::Tree, inline_parser: &mut inline::Parser<'s>) -> Self {
 | 
				
			||||||
        src: &'s str,
 | 
					 | 
				
			||||||
        mut tree: block::Tree,
 | 
					 | 
				
			||||||
        inline_parser: &mut inline::Parser<span::InlineCharsIter<'s>>,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					 | 
				
			||||||
        let mut link_definitions = Map::new();
 | 
					        let mut link_definitions = Map::new();
 | 
				
			||||||
        let mut headings: Vec<Heading> = Vec::new();
 | 
					        let mut headings: Vec<Heading> = Vec::new();
 | 
				
			||||||
        let mut used_ids: Set<&str> = Set::new();
 | 
					        let mut used_ids: Set<&str> = Set::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut inlines = span::InlineSpans::new(src);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut attr_prev: Option<Span> = None;
 | 
					        let mut attr_prev: Option<Span> = None;
 | 
				
			||||||
        while let Some(e) = tree.next() {
 | 
					        while let Some(e) = tree.next() {
 | 
				
			||||||
            match e.kind {
 | 
					            match e.kind {
 | 
				
			||||||
| 
						 | 
					@ -668,32 +662,35 @@ impl<'s> PrePass<'s> {
 | 
				
			||||||
                        .and_then(|attrs| attrs.get("id"))
 | 
					                        .and_then(|attrs| attrs.get("id"))
 | 
				
			||||||
                        .map(ToString::to_string);
 | 
					                        .map(ToString::to_string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    inlines.set_spans(tree.take_inlines());
 | 
					 | 
				
			||||||
                    let mut id_auto = String::new();
 | 
					                    let mut id_auto = String::new();
 | 
				
			||||||
                    let mut last_whitespace = true;
 | 
					                    let mut last_whitespace = true;
 | 
				
			||||||
                    inline_parser.reset(inlines.chars());
 | 
					                    let inlines = tree.take_inlines().collect::<Vec<_>>();
 | 
				
			||||||
                    inline_parser.for_each(|ev| match ev.kind {
 | 
					                    inline_parser.reset();
 | 
				
			||||||
                        inline::EventKind::Str => {
 | 
					                    inlines.iter().enumerate().for_each(|(i, sp)| {
 | 
				
			||||||
                            let mut chars = inlines.slice(ev.span).chars().peekable();
 | 
					                        inline_parser.feed_line(sp.of(src), sp.start(), i == inlines.len() - 1);
 | 
				
			||||||
                            while let Some(c) = chars.next() {
 | 
					                        inline_parser.for_each(|ev| match ev.kind {
 | 
				
			||||||
                                if c.is_whitespace() {
 | 
					                            inline::EventKind::Str => {
 | 
				
			||||||
                                    while chars.peek().map_or(false, |c| c.is_whitespace()) {
 | 
					                                let mut chars = ev.span.of(src).chars().peekable();
 | 
				
			||||||
                                        chars.next();
 | 
					                                while let Some(c) = chars.next() {
 | 
				
			||||||
 | 
					                                    if c.is_whitespace() {
 | 
				
			||||||
 | 
					                                        while chars.peek().map_or(false, |c| c.is_whitespace()) {
 | 
				
			||||||
 | 
					                                            chars.next();
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        if !last_whitespace {
 | 
				
			||||||
 | 
					                                            last_whitespace = true;
 | 
				
			||||||
 | 
					                                            id_auto.push('-');
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    } else if !c.is_ascii_punctuation() || matches!(c, '-' | '_') {
 | 
				
			||||||
 | 
					                                        id_auto.push(c);
 | 
				
			||||||
 | 
					                                        last_whitespace = false;
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    if !last_whitespace {
 | 
					 | 
				
			||||||
                                        last_whitespace = true;
 | 
					 | 
				
			||||||
                                        id_auto.push('-');
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                } else if !c.is_ascii_punctuation() || matches!(c, '-' | '_') {
 | 
					 | 
				
			||||||
                                    id_auto.push(c);
 | 
					 | 
				
			||||||
                                    last_whitespace = false;
 | 
					 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                            inline::EventKind::Atom(inline::Atom::Softbreak) => {
 | 
				
			||||||
                        inline::EventKind::Atom(inline::Atom::Softbreak) => {
 | 
					                                id_auto.push('-');
 | 
				
			||||||
                            id_auto.push('-');
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                            _ => {}
 | 
				
			||||||
                        _ => {}
 | 
					                        })
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    id_auto.drain(id_auto.trim_end_matches('-').len()..);
 | 
					                    id_auto.drain(id_auto.trim_end_matches('-').len()..);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -772,7 +769,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn new(src: &'s str) -> Self {
 | 
					    pub fn new(src: &'s str) -> Self {
 | 
				
			||||||
        let tree = block::parse(src);
 | 
					        let tree = block::parse(src);
 | 
				
			||||||
        let mut inline_parser = inline::Parser::new(span::InlineChars::empty(src));
 | 
					        let mut inline_parser = inline::Parser::new();
 | 
				
			||||||
        let pre_pass = PrePass::new(src, tree.clone(), &mut inline_parser);
 | 
					        let pre_pass = PrePass::new(src, tree.clone(), &mut inline_parser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
| 
						 | 
					@ -781,11 +778,11 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
            pre_pass,
 | 
					            pre_pass,
 | 
				
			||||||
            block_attributes: Attributes::new(),
 | 
					            block_attributes: Attributes::new(),
 | 
				
			||||||
            table_head_row: false,
 | 
					            table_head_row: false,
 | 
				
			||||||
 | 
					            verbatim: false,
 | 
				
			||||||
            footnote_references: Vec::new(),
 | 
					            footnote_references: Vec::new(),
 | 
				
			||||||
            footnotes: Map::new(),
 | 
					            footnotes: Map::new(),
 | 
				
			||||||
            footnote_index: 0,
 | 
					            footnote_index: 0,
 | 
				
			||||||
            footnote_active: false,
 | 
					            footnote_active: false,
 | 
				
			||||||
            inlines: span::InlineSpans::new(src),
 | 
					 | 
				
			||||||
            inline_parser,
 | 
					            inline_parser,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -799,7 +796,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
        let mut attributes = inline.as_ref().map_or_else(Attributes::new, |inl| {
 | 
					        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;
 | 
					                first_is_attr = true;
 | 
				
			||||||
                attr::parse(self.inlines.slice(inl.span))
 | 
					                attr::parse(inl.span.of(self.src))
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                Attributes::new()
 | 
					                Attributes::new()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -819,10 +816,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                        inline::Container::InlineMath => Container::Math { display: false },
 | 
					                        inline::Container::InlineMath => Container::Math { display: false },
 | 
				
			||||||
                        inline::Container::DisplayMath => Container::Math { display: true },
 | 
					                        inline::Container::DisplayMath => Container::Math { display: true },
 | 
				
			||||||
                        inline::Container::RawFormat => Container::RawInline {
 | 
					                        inline::Container::RawFormat => Container::RawInline {
 | 
				
			||||||
                            format: match self.inlines.src(inline.span) {
 | 
					                            format: inline.span.of(self.src),
 | 
				
			||||||
                                CowStr::Owned(_) => panic!(),
 | 
					 | 
				
			||||||
                                CowStr::Borrowed(s) => s,
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        inline::Container::Subscript => Container::Subscript,
 | 
					                        inline::Container::Subscript => Container::Subscript,
 | 
				
			||||||
                        inline::Container::Superscript => Container::Superscript,
 | 
					                        inline::Container::Superscript => Container::Superscript,
 | 
				
			||||||
| 
						 | 
					@ -832,33 +826,27 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                        inline::Container::Strong => Container::Strong,
 | 
					                        inline::Container::Strong => Container::Strong,
 | 
				
			||||||
                        inline::Container::Mark => Container::Mark,
 | 
					                        inline::Container::Mark => Container::Mark,
 | 
				
			||||||
                        inline::Container::InlineLink => Container::Link(
 | 
					                        inline::Container::InlineLink => Container::Link(
 | 
				
			||||||
                            match self.inlines.src(inline.span) {
 | 
					                            inline.span.of(self.src).replace('\n', "").into(),
 | 
				
			||||||
                                CowStr::Owned(s) => s.replace('\n', "").into(),
 | 
					 | 
				
			||||||
                                s @ CowStr::Borrowed(_) => s,
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                            LinkType::Span(SpanLinkType::Inline),
 | 
					                            LinkType::Span(SpanLinkType::Inline),
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
                        inline::Container::InlineImage => Container::Image(
 | 
					                        inline::Container::InlineImage => Container::Image(
 | 
				
			||||||
                            match self.inlines.src(inline.span) {
 | 
					                            inline.span.of(self.src).replace('\n', "").into(),
 | 
				
			||||||
                                CowStr::Owned(s) => s.replace('\n', "").into(),
 | 
					 | 
				
			||||||
                                s @ CowStr::Borrowed(_) => s,
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                            SpanLinkType::Inline,
 | 
					                            SpanLinkType::Inline,
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
                        inline::Container::ReferenceLink | inline::Container::ReferenceImage => {
 | 
					                        inline::Container::ReferenceLink | inline::Container::ReferenceImage => {
 | 
				
			||||||
                            let tag = match self.inlines.src(inline.span) {
 | 
					                            let tag = inline.span.of(self.src).replace('\n', " ");
 | 
				
			||||||
                                CowStr::Owned(s) => s.replace('\n', " ").into(),
 | 
					                            let link_def = self
 | 
				
			||||||
                                s @ CowStr::Borrowed(_) => s,
 | 
					                                .pre_pass
 | 
				
			||||||
                            };
 | 
					                                .link_definitions
 | 
				
			||||||
                            let link_def =
 | 
					                                .get::<str>(tag.as_ref())
 | 
				
			||||||
                                self.pre_pass.link_definitions.get(tag.as_ref()).cloned();
 | 
					                                .cloned();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            let (url_or_tag, ty) = if let Some((url, attrs_def)) = link_def {
 | 
					                            let (url_or_tag, ty) = if let Some((url, attrs_def)) = link_def {
 | 
				
			||||||
                                attributes.union(attrs_def);
 | 
					                                attributes.union(attrs_def);
 | 
				
			||||||
                                (url, SpanLinkType::Reference)
 | 
					                                (url, SpanLinkType::Reference)
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                self.pre_pass.heading_id_by_tag(tag.as_ref()).map_or_else(
 | 
					                                self.pre_pass.heading_id_by_tag(tag.as_ref()).map_or_else(
 | 
				
			||||||
                                    || (tag, SpanLinkType::Unresolved),
 | 
					                                    || (tag.into(), SpanLinkType::Unresolved),
 | 
				
			||||||
                                    |id| (format!("#{}", id).into(), SpanLinkType::Reference),
 | 
					                                    |id| (format!("#{}", id).into(), SpanLinkType::Reference),
 | 
				
			||||||
                                )
 | 
					                                )
 | 
				
			||||||
                            };
 | 
					                            };
 | 
				
			||||||
| 
						 | 
					@ -870,7 +858,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        inline::Container::Autolink => {
 | 
					                        inline::Container::Autolink => {
 | 
				
			||||||
                            let url = self.inlines.src(inline.span);
 | 
					                            let url: CowStr = inline.span.of(self.src).into();
 | 
				
			||||||
                            let ty = if url.contains('@') {
 | 
					                            let ty = if url.contains('@') {
 | 
				
			||||||
                                LinkType::Email
 | 
					                                LinkType::Email
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
| 
						 | 
					@ -887,10 +875,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                inline::EventKind::Atom(a) => match a {
 | 
					                inline::EventKind::Atom(a) => match a {
 | 
				
			||||||
                    inline::Atom::FootnoteReference => {
 | 
					                    inline::Atom::FootnoteReference => {
 | 
				
			||||||
                        let tag = match self.inlines.src(inline.span) {
 | 
					                        let tag = inline.span.of(self.src);
 | 
				
			||||||
                            CowStr::Borrowed(s) => s,
 | 
					 | 
				
			||||||
                            CowStr::Owned(..) => panic!(),
 | 
					 | 
				
			||||||
                        };
 | 
					 | 
				
			||||||
                        let number = self
 | 
					                        let number = self
 | 
				
			||||||
                            .footnote_references
 | 
					                            .footnote_references
 | 
				
			||||||
                            .iter()
 | 
					                            .iter()
 | 
				
			||||||
| 
						 | 
					@ -902,15 +887,9 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                },
 | 
					                                },
 | 
				
			||||||
                                |i| i + 1,
 | 
					                                |i| i + 1,
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
                        Event::FootnoteReference(
 | 
					                        Event::FootnoteReference(inline.span.of(self.src), number)
 | 
				
			||||||
                            match self.inlines.src(inline.span) {
 | 
					 | 
				
			||||||
                                CowStr::Borrowed(s) => s,
 | 
					 | 
				
			||||||
                                CowStr::Owned(..) => panic!(),
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                            number,
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    inline::Atom::Symbol => Event::Symbol(self.inlines.src(inline.span)),
 | 
					                    inline::Atom::Symbol => Event::Symbol(inline.span.of(self.src).into()),
 | 
				
			||||||
                    inline::Atom::Quote { ty, left } => match (ty, left) {
 | 
					                    inline::Atom::Quote { ty, left } => match (ty, left) {
 | 
				
			||||||
                        (inline::QuoteType::Single, true) => Event::LeftSingleQuote,
 | 
					                        (inline::QuoteType::Single, true) => Event::LeftSingleQuote,
 | 
				
			||||||
                        (inline::QuoteType::Single, false) => Event::RightSingleQuote,
 | 
					                        (inline::QuoteType::Single, false) => Event::RightSingleQuote,
 | 
				
			||||||
| 
						 | 
					@ -925,7 +904,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                    inline::Atom::Hardbreak => Event::Hardbreak,
 | 
					                    inline::Atom::Hardbreak => Event::Hardbreak,
 | 
				
			||||||
                    inline::Atom::Escape => Event::Escape,
 | 
					                    inline::Atom::Escape => Event::Escape,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                inline::EventKind::Str => Event::Str(self.inlines.src(inline.span)),
 | 
					                inline::EventKind::Str => Event::Str(inline.span.of(self.src).into()),
 | 
				
			||||||
                inline::EventKind::Whitespace
 | 
					                inline::EventKind::Whitespace
 | 
				
			||||||
                | inline::EventKind::Attributes
 | 
					                | inline::EventKind::Attributes
 | 
				
			||||||
                | inline::EventKind::Placeholder => {
 | 
					                | inline::EventKind::Placeholder => {
 | 
				
			||||||
| 
						 | 
					@ -953,6 +932,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                    let enter = matches!(ev.kind, tree::EventKind::Enter(..));
 | 
					                    let enter = matches!(ev.kind, tree::EventKind::Enter(..));
 | 
				
			||||||
                    let cont = match c {
 | 
					                    let cont = match c {
 | 
				
			||||||
                        block::Node::Leaf(l) => {
 | 
					                        block::Node::Leaf(l) => {
 | 
				
			||||||
 | 
					                            self.inline_parser.reset();
 | 
				
			||||||
                            if matches!(l, block::Leaf::LinkDefinition) {
 | 
					                            if matches!(l, block::Leaf::LinkDefinition) {
 | 
				
			||||||
                                // ignore link definitions
 | 
					                                // ignore link definitions
 | 
				
			||||||
                                if enter {
 | 
					                                if enter {
 | 
				
			||||||
| 
						 | 
					@ -961,10 +941,6 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                self.block_attributes = Attributes::new();
 | 
					                                self.block_attributes = Attributes::new();
 | 
				
			||||||
                                continue;
 | 
					                                continue;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if enter && !matches!(l, block::Leaf::CodeBlock) {
 | 
					 | 
				
			||||||
                                self.inlines.set_spans(self.tree.take_inlines());
 | 
					 | 
				
			||||||
                                self.inline_parser.reset(self.inlines.chars());
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            match l {
 | 
					                            match l {
 | 
				
			||||||
                                block::Leaf::Paragraph => Container::Paragraph,
 | 
					                                block::Leaf::Paragraph => Container::Paragraph,
 | 
				
			||||||
                                block::Leaf::Heading { has_section } => Container::Heading {
 | 
					                                block::Leaf::Heading { has_section } => Container::Heading {
 | 
				
			||||||
| 
						 | 
					@ -979,6 +955,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                },
 | 
					                                },
 | 
				
			||||||
                                block::Leaf::DescriptionTerm => Container::DescriptionTerm,
 | 
					                                block::Leaf::DescriptionTerm => Container::DescriptionTerm,
 | 
				
			||||||
                                block::Leaf::CodeBlock => {
 | 
					                                block::Leaf::CodeBlock => {
 | 
				
			||||||
 | 
					                                    self.verbatim = enter;
 | 
				
			||||||
                                    if let Some(format) = content.strip_prefix('=') {
 | 
					                                    if let Some(format) = content.strip_prefix('=') {
 | 
				
			||||||
                                        Container::RawBlock { format }
 | 
					                                        Container::RawBlock { format }
 | 
				
			||||||
                                    } else {
 | 
					                                    } else {
 | 
				
			||||||
| 
						 | 
					@ -1058,7 +1035,18 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                        Event::End(cont)
 | 
					                        Event::End(cont)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                tree::EventKind::Inline => Event::Str(content.into()), // verbatim
 | 
					                tree::EventKind::Inline => {
 | 
				
			||||||
 | 
					                    if self.verbatim {
 | 
				
			||||||
 | 
					                        Event::Str(content.into())
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        self.inline_parser.feed_line(
 | 
				
			||||||
 | 
					                            content,
 | 
				
			||||||
 | 
					                            ev.span.start(),
 | 
				
			||||||
 | 
					                            self.tree.branch_is_empty(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                        return self.next();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            return Some(event);
 | 
					            return Some(event);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -1314,7 +1302,8 @@ mod test {
 | 
				
			||||||
            Start(Blockquote, Attributes::new()),
 | 
					            Start(Blockquote, Attributes::new()),
 | 
				
			||||||
            Start(Paragraph, Attributes::new()),
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
            Start(Verbatim, Attributes::new()),
 | 
					            Start(Verbatim, Attributes::new()),
 | 
				
			||||||
            Str("abc\ndef".into()),
 | 
					            Str("abc\n".into()),
 | 
				
			||||||
 | 
					            Str("def".into()),
 | 
				
			||||||
            End(Verbatim),
 | 
					            End(Verbatim),
 | 
				
			||||||
            End(Paragraph),
 | 
					            End(Paragraph),
 | 
				
			||||||
            End(Blockquote),
 | 
					            End(Blockquote),
 | 
				
			||||||
| 
						 | 
					@ -1368,6 +1357,11 @@ mod test {
 | 
				
			||||||
            End(Link("url".into(), LinkType::Span(SpanLinkType::Inline))),
 | 
					            End(Link("url".into(), LinkType::Span(SpanLinkType::Inline))),
 | 
				
			||||||
            End(Paragraph),
 | 
					            End(Paragraph),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[ignore = "broken"]
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn link_inline_multi_line() {
 | 
				
			||||||
        test_parse!(
 | 
					        test_parse!(
 | 
				
			||||||
            concat!(
 | 
					            concat!(
 | 
				
			||||||
                "> [text](url\n",
 | 
					                "> [text](url\n",
 | 
				
			||||||
| 
						 | 
					@ -1448,6 +1442,7 @@ mod test {
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[ignore = "multiline links broken"]
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn link_reference_multiline() {
 | 
					    fn link_reference_multiline() {
 | 
				
			||||||
        test_parse!(
 | 
					        test_parse!(
 | 
				
			||||||
| 
						 | 
					@ -1765,6 +1760,7 @@ mod test {
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[ignore = "multiline attributes broken"]
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn attr_inline_multiline() {
 | 
					    fn attr_inline_multiline() {
 | 
				
			||||||
        test_parse!(
 | 
					        test_parse!(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										207
									
								
								src/span.rs
									
										
									
									
									
								
							
							
						
						
									
										207
									
								
								src/span.rs
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,3 @@
 | 
				
			||||||
use crate::CowStr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
 | 
					#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
 | 
				
			||||||
pub struct Span {
 | 
					pub struct Span {
 | 
				
			||||||
    start: u32,
 | 
					    start: u32,
 | 
				
			||||||
| 
						 | 
					@ -107,211 +105,6 @@ impl Span {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait DiscontinuousString<'s> {
 | 
					 | 
				
			||||||
    type Chars: Iterator<Item = char>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn src(&self, span: Span) -> CowStr<'s>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn chars(&self) -> Self::Chars;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s> DiscontinuousString<'s> for &'s str {
 | 
					 | 
				
			||||||
    type Chars = std::str::Chars<'s>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn src(&self, span: Span) -> CowStr<'s> {
 | 
					 | 
				
			||||||
        span.of(self).into()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn chars(&self) -> Self::Chars {
 | 
					 | 
				
			||||||
        str::chars(self)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Multiple discontinuous [`std::str::Chars`] objects concatenated.
 | 
					 | 
				
			||||||
#[derive(Clone)]
 | 
					 | 
				
			||||||
pub struct InlineChars<'s, I> {
 | 
					 | 
				
			||||||
    src: &'s str,
 | 
					 | 
				
			||||||
    inlines: I,
 | 
					 | 
				
			||||||
    next: std::str::Chars<'s>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Implement inlines.flat_map(|sp| sp.of(self.src).chars())
 | 
					 | 
				
			||||||
impl<'s, I: Iterator<Item = Span>> InlineChars<'s, I> {
 | 
					 | 
				
			||||||
    fn new(src: &'s str, inlines: I) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            src,
 | 
					 | 
				
			||||||
            inlines,
 | 
					 | 
				
			||||||
            next: "".chars(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s, I: Iterator<Item = Span>> Iterator for InlineChars<'s, I> {
 | 
					 | 
				
			||||||
    type Item = char;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
					 | 
				
			||||||
        if self.next.as_str().is_empty() {
 | 
					 | 
				
			||||||
            self.next = self
 | 
					 | 
				
			||||||
                .inlines
 | 
					 | 
				
			||||||
                .next()
 | 
					 | 
				
			||||||
                .map_or_else(|| "".chars(), |sp| sp.of(self.src).chars());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        self.next.next()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type InlineCharsIter<'s> = InlineChars<'s, std::iter::Copied<std::slice::Iter<'static, Span>>>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s> InlineCharsIter<'s> {
 | 
					 | 
				
			||||||
    pub fn empty(src: &'s str) -> Self {
 | 
					 | 
				
			||||||
        InlineChars::new(src, [].iter().copied())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Discontinuous slices of a [`&str`].
 | 
					 | 
				
			||||||
#[derive(Default, Debug)]
 | 
					 | 
				
			||||||
pub struct InlineSpans<'s> {
 | 
					 | 
				
			||||||
    src: &'s str,
 | 
					 | 
				
			||||||
    spans: Vec<Span>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s> InlineSpans<'s> {
 | 
					 | 
				
			||||||
    pub fn new(src: &'s str) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            src,
 | 
					 | 
				
			||||||
            spans: Vec::new(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn set_spans(&mut self, spans: impl Iterator<Item = Span>) {
 | 
					 | 
				
			||||||
        self.spans.clear();
 | 
					 | 
				
			||||||
        self.spans.extend(spans);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn slice<'i>(&'i self, span: Span) -> InlineSpansSlice<'s, 'i> {
 | 
					 | 
				
			||||||
        let mut first = 0;
 | 
					 | 
				
			||||||
        let mut last = 0;
 | 
					 | 
				
			||||||
        let mut first_skip = 0;
 | 
					 | 
				
			||||||
        let mut last_len = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut a = 0;
 | 
					 | 
				
			||||||
        for (i, sp) in self.spans.iter().enumerate() {
 | 
					 | 
				
			||||||
            let b = a + sp.len();
 | 
					 | 
				
			||||||
            if span.start() < b {
 | 
					 | 
				
			||||||
                if a <= span.start() {
 | 
					 | 
				
			||||||
                    first = i;
 | 
					 | 
				
			||||||
                    first_skip = span.start() - a;
 | 
					 | 
				
			||||||
                    if span.end() <= b {
 | 
					 | 
				
			||||||
                        // continuous
 | 
					 | 
				
			||||||
                        last = i;
 | 
					 | 
				
			||||||
                        last_len = span.len();
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    last = i;
 | 
					 | 
				
			||||||
                    last_len = sp.len().min(span.end() - a);
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            a = b;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_ne!(last_len, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InlineSpansSlice {
 | 
					 | 
				
			||||||
            src: self.src,
 | 
					 | 
				
			||||||
            first_skip,
 | 
					 | 
				
			||||||
            last_len,
 | 
					 | 
				
			||||||
            spans: &self.spans[first..=last],
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Borrow if continuous, copy if discontiunous.
 | 
					 | 
				
			||||||
    fn borrow_or_copy<I: Iterator<Item = Span>>(src: &str, spans: I, span: Span) -> CowStr {
 | 
					 | 
				
			||||||
        let mut a = 0;
 | 
					 | 
				
			||||||
        let mut s = String::new();
 | 
					 | 
				
			||||||
        for sp in spans {
 | 
					 | 
				
			||||||
            let b = a + sp.len();
 | 
					 | 
				
			||||||
            if span.start() < b {
 | 
					 | 
				
			||||||
                let r = if a <= span.start() {
 | 
					 | 
				
			||||||
                    if span.end() <= b {
 | 
					 | 
				
			||||||
                        // continuous
 | 
					 | 
				
			||||||
                        return CowStr::Borrowed(&sp.of(src)[span.start() - a..span.end() - a]);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    (span.start() - a)..sp.len()
 | 
					 | 
				
			||||||
                } else if a <= span.end() {
 | 
					 | 
				
			||||||
                    0..sp.len().min(span.end() - a)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                s.push_str(&sp.of(src)[r]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            a = b;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        assert_eq!(span.len(), s.len());
 | 
					 | 
				
			||||||
        CowStr::Owned(s)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s> DiscontinuousString<'s> for InlineSpans<'s> {
 | 
					 | 
				
			||||||
    type Chars = InlineCharsIter<'s>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn src(&self, span: Span) -> CowStr<'s> {
 | 
					 | 
				
			||||||
        Self::borrow_or_copy(self.src, self.spans.iter().copied(), span)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn chars(&self) -> Self::Chars {
 | 
					 | 
				
			||||||
        // SAFETY: do not call set_spans while chars is in use
 | 
					 | 
				
			||||||
        unsafe { std::mem::transmute(InlineChars::new(self.src, self.spans.iter().copied())) }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A read-only slice of an [`InlineSpans`] object.
 | 
					 | 
				
			||||||
pub struct InlineSpansSlice<'s, 'i> {
 | 
					 | 
				
			||||||
    src: &'s str,
 | 
					 | 
				
			||||||
    first_skip: usize,
 | 
					 | 
				
			||||||
    last_len: usize,
 | 
					 | 
				
			||||||
    spans: &'i [Span],
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s, 'i> InlineSpansSlice<'s, 'i> {
 | 
					 | 
				
			||||||
    fn spans(&self) -> InlineSpansSliceIter<'i> {
 | 
					 | 
				
			||||||
        let (span_start, r_middle, span_end) = if self.spans.len() == 1 {
 | 
					 | 
				
			||||||
            (
 | 
					 | 
				
			||||||
                Span::by_len(self.spans[0].start() + self.first_skip, self.last_len),
 | 
					 | 
				
			||||||
                0..0,
 | 
					 | 
				
			||||||
                Span::by_len(self.spans[self.spans.len() - 1].start(), 0),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            (
 | 
					 | 
				
			||||||
                Span::new(self.spans[0].start() + self.first_skip, self.spans[0].end()),
 | 
					 | 
				
			||||||
                1..1 + self.spans.len().saturating_sub(2),
 | 
					 | 
				
			||||||
                Span::by_len(self.spans[self.spans.len() - 1].start(), self.last_len),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        std::iter::once(span_start)
 | 
					 | 
				
			||||||
            .chain(self.spans[r_middle].iter().copied())
 | 
					 | 
				
			||||||
            .chain(std::iter::once(span_end))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s, 'i> DiscontinuousString<'s> for InlineSpansSlice<'s, 'i> {
 | 
					 | 
				
			||||||
    type Chars = InlineChars<'s, InlineSpansSliceIter<'i>>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn src(&self, span: Span) -> CowStr<'s> {
 | 
					 | 
				
			||||||
        InlineSpans::borrow_or_copy(self.src, self.spans(), span)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn chars(&self) -> Self::Chars {
 | 
					 | 
				
			||||||
        InlineChars::new(self.src, self.spans())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type InlineSpansSliceIter<'i> = std::iter::Chain<
 | 
					 | 
				
			||||||
    std::iter::Chain<std::iter::Once<Span>, std::iter::Copied<std::slice::Iter<'i, Span>>>,
 | 
					 | 
				
			||||||
    std::iter::Once<Span>,
 | 
					 | 
				
			||||||
>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod test {
 | 
					mod test {
 | 
				
			||||||
    use super::Span;
 | 
					    use super::Span;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,10 @@ impl<C: Clone, A: Clone> Tree<C, A> {
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn branch_is_empty(&self) -> bool {
 | 
				
			||||||
 | 
					        matches!(self.head, None)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<C: Clone, A: Clone> Iterator for Tree<C, A> {
 | 
					impl<C: Clone, A: Clone> Iterator for Tree<C, A> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue