raw inline format
This commit is contained in:
		
					parent
					
						
							
								72bedb53b4
							
						
					
				
			
			
				commit
				
					
						e798dc9c28
					
				
			
		
					 4 changed files with 154 additions and 73 deletions
				
			
		
							
								
								
									
										35
									
								
								src/html.rs
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								src/html.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -42,14 +42,25 @@ pub fn write<'s, I: Iterator<Item = Event<'s>>, W: std::io::Write>(
 | 
			
		|||
        .map_err(|_| output.error.unwrap_err())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Raw {
 | 
			
		||||
    None,
 | 
			
		||||
    Html,
 | 
			
		||||
    Other,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Writer<I, W> {
 | 
			
		||||
    events: I,
 | 
			
		||||
    out: W,
 | 
			
		||||
    raw: Raw,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		||||
    fn new(events: I, out: W) -> Self {
 | 
			
		||||
        Self { events, out }
 | 
			
		||||
        Self {
 | 
			
		||||
            events,
 | 
			
		||||
            out,
 | 
			
		||||
            raw: Raw::None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write(&mut self) -> std::fmt::Result {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +90,6 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		|||
                        Container::Heading { level } => write!(self.out, "<h{}>", level)?,
 | 
			
		||||
                        Container::TableCell => self.out.write_str("<td>")?,
 | 
			
		||||
                        Container::DescriptionTerm => self.out.write_str("<dt>")?,
 | 
			
		||||
                        Container::RawBlock { .. } => todo!(),
 | 
			
		||||
                        Container::CodeBlock { lang } => {
 | 
			
		||||
                            if let Some(l) = lang {
 | 
			
		||||
                                write!(self.out, r#"<pre><code class="language-{}">"#, l)?;
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +106,13 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		|||
                        } else {
 | 
			
		||||
                            r#"<span class="math inline">\("#
 | 
			
		||||
                        })?,
 | 
			
		||||
                        Container::RawInline { .. } => todo!(),
 | 
			
		||||
                        Container::RawBlock { format } | Container::RawInline { format } => {
 | 
			
		||||
                            self.raw = if format == "html" {
 | 
			
		||||
                                Raw::Html
 | 
			
		||||
                            } else {
 | 
			
		||||
                                Raw::Other
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        Container::Subscript => self.out.write_str("<sub>")?,
 | 
			
		||||
                        Container::Superscript => self.out.write_str("<sup>")?,
 | 
			
		||||
                        Container::Insert => self.out.write_str("<ins>")?,
 | 
			
		||||
| 
						 | 
				
			
			@ -126,14 +142,15 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		|||
                        Container::Heading { level } => write!(self.out, "</h{}>", level)?,
 | 
			
		||||
                        Container::TableCell => self.out.write_str("</td>")?,
 | 
			
		||||
                        Container::DescriptionTerm => self.out.write_str("</dt>")?,
 | 
			
		||||
                        Container::RawBlock { .. } => todo!(),
 | 
			
		||||
                        Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
 | 
			
		||||
                        Container::Span => self.out.write_str("</span>")?,
 | 
			
		||||
                        Container::Link(..) => todo!(),
 | 
			
		||||
                        Container::Image(..) => todo!(),
 | 
			
		||||
                        Container::Verbatim => self.out.write_str("</code>")?,
 | 
			
		||||
                        Container::Math { .. } => self.out.write_str("</span>")?,
 | 
			
		||||
                        Container::RawInline { .. } => todo!(),
 | 
			
		||||
                        Container::RawBlock { .. } | Container::RawInline { .. } => {
 | 
			
		||||
                            self.raw = Raw::None
 | 
			
		||||
                        }
 | 
			
		||||
                        Container::Subscript => self.out.write_str("</sub>")?,
 | 
			
		||||
                        Container::Superscript => self.out.write_str("</sup>")?,
 | 
			
		||||
                        Container::Insert => self.out.write_str("</ins>")?,
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +162,8 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		|||
                        Container::DoubleQuoted => self.out.write_str("”")?,
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Event::Str(mut s) => {
 | 
			
		||||
                Event::Str(mut s) => match self.raw {
 | 
			
		||||
                    Raw::None => {
 | 
			
		||||
                        let mut ent = "";
 | 
			
		||||
                        while let Some(i) = s.chars().position(|c| {
 | 
			
		||||
                            if let Some(s) = match c {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +185,11 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
 | 
			
		|||
                        }
 | 
			
		||||
                        self.out.write_str(s)?;
 | 
			
		||||
                    }
 | 
			
		||||
                    Raw::Html => {
 | 
			
		||||
                        self.out.write_str(s)?;
 | 
			
		||||
                    }
 | 
			
		||||
                    Raw::Other => {}
 | 
			
		||||
                },
 | 
			
		||||
 | 
			
		||||
                Event::Atom(a) => match a {
 | 
			
		||||
                    Atom::Ellipsis => self.out.write_str("…")?,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,12 +60,14 @@ pub struct Event {
 | 
			
		|||
 | 
			
		||||
/// Current parsing state of elements that are not recursive, i.e. may not contain arbitrary inline
 | 
			
		||||
/// elements, can only be one of these at a time.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
enum State {
 | 
			
		||||
    None,
 | 
			
		||||
    /// Within a verbatim element, e.g. '$`xxxxx'
 | 
			
		||||
    Verbatim {
 | 
			
		||||
        kind: Container,
 | 
			
		||||
        opener_len: usize,
 | 
			
		||||
        opener_event: usize,
 | 
			
		||||
    },
 | 
			
		||||
    /// Potentially within an attribute list, e.g. '{a=b '.
 | 
			
		||||
    Attributes {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,9 +83,14 @@ enum State {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    fn verbatim(&self) -> Option<(Container, usize)> {
 | 
			
		||||
        if let Self::Verbatim { kind, opener_len } = self {
 | 
			
		||||
            Some((*kind, *opener_len))
 | 
			
		||||
    fn verbatim(&self) -> Option<(Container, usize, usize)> {
 | 
			
		||||
        if let Self::Verbatim {
 | 
			
		||||
            kind,
 | 
			
		||||
            opener_len,
 | 
			
		||||
            opener_event,
 | 
			
		||||
        } = self
 | 
			
		||||
        {
 | 
			
		||||
            Some((*kind, *opener_len, *opener_event))
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -173,16 +180,34 @@ impl<'s> Parser<'s> {
 | 
			
		|||
    fn parse_verbatim(&mut self, first: &lex::Token) -> Option<Event> {
 | 
			
		||||
        self.state
 | 
			
		||||
            .verbatim()
 | 
			
		||||
            .map(|(kind, opener_len)| {
 | 
			
		||||
            .map(|(kind, opener_len, opener_event)| {
 | 
			
		||||
                dbg!(&self.events, opener_event);
 | 
			
		||||
                assert_eq!(self.events[opener_event].kind, EventKind::Enter(kind));
 | 
			
		||||
                let kind = if matches!(first.kind, lex::Kind::Seq(lex::Sequence::Backtick))
 | 
			
		||||
                    && first.len == opener_len
 | 
			
		||||
                {
 | 
			
		||||
                    self.state = State::None;
 | 
			
		||||
                    if matches!(kind, Container::Span) {
 | 
			
		||||
                        todo!()
 | 
			
		||||
                    let kind =
 | 
			
		||||
                        if matches!(kind, Verbatim) && self.lexer.peek_ahead().starts_with("{=") {
 | 
			
		||||
                            let mut chars = self.lexer.peek_ahead()[2..].chars();
 | 
			
		||||
                            let len = chars
 | 
			
		||||
                                .clone()
 | 
			
		||||
                                .take_while(|c| !c.is_whitespace() && !matches!(c, '{' | '}'))
 | 
			
		||||
                                .count();
 | 
			
		||||
                            if len > 0 && chars.nth(len) == Some('}') {
 | 
			
		||||
                                self.lexer = lex::Lexer::new(chars.as_str());
 | 
			
		||||
                                let span_format = Span::by_len(self.span.end() + "{=".len(), len);
 | 
			
		||||
                                self.events[opener_event].kind = EventKind::Enter(RawFormat);
 | 
			
		||||
                                self.events[opener_event].span = span_format;
 | 
			
		||||
                                self.span = span_format;
 | 
			
		||||
                                RawFormat
 | 
			
		||||
                            } else {
 | 
			
		||||
                        EventKind::Exit(kind)
 | 
			
		||||
                                Verbatim
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            kind
 | 
			
		||||
                        };
 | 
			
		||||
                    EventKind::Exit(kind)
 | 
			
		||||
                } else {
 | 
			
		||||
                    EventKind::Str
 | 
			
		||||
                };
 | 
			
		||||
| 
						 | 
				
			
			@ -203,9 +228,9 @@ impl<'s> Parser<'s> {
 | 
			
		|||
                                {
 | 
			
		||||
                                    Some((
 | 
			
		||||
                                        if first.len == 2 {
 | 
			
		||||
                                            Container::DisplayMath
 | 
			
		||||
                                            DisplayMath
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            Container::InlineMath
 | 
			
		||||
                                            InlineMath
 | 
			
		||||
                                        },
 | 
			
		||||
                                        *len,
 | 
			
		||||
                                    ))
 | 
			
		||||
| 
						 | 
				
			
			@ -219,13 +244,16 @@ impl<'s> Parser<'s> {
 | 
			
		|||
                        }
 | 
			
		||||
                        math_opt
 | 
			
		||||
                    }
 | 
			
		||||
                    lex::Kind::Seq(lex::Sequence::Backtick) => {
 | 
			
		||||
                        Some((Container::Verbatim, first.len))
 | 
			
		||||
                    }
 | 
			
		||||
                    lex::Kind::Seq(lex::Sequence::Backtick) => Some((Verbatim, first.len)),
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                }
 | 
			
		||||
                .map(|(kind, opener_len)| {
 | 
			
		||||
                    self.state = State::Verbatim { kind, opener_len };
 | 
			
		||||
                    dbg!(&self.events);
 | 
			
		||||
                    self.state = State::Verbatim {
 | 
			
		||||
                        kind,
 | 
			
		||||
                        opener_len,
 | 
			
		||||
                        opener_event: self.events.len(),
 | 
			
		||||
                    };
 | 
			
		||||
                    Event {
 | 
			
		||||
                        kind: EventKind::Enter(kind),
 | 
			
		||||
                        span: self.span,
 | 
			
		||||
| 
						 | 
				
			
			@ -295,20 +323,25 @@ impl<'s> Iterator for Parser<'s> {
 | 
			
		|||
    type Item = Event;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        let mut need_more = false;
 | 
			
		||||
        while self.events.is_empty()
 | 
			
		||||
            || !self.openers.is_empty()
 | 
			
		||||
            || self
 | 
			
		||||
            || !matches!(self.state, State::None)
 | 
			
		||||
            || self // for merge
 | 
			
		||||
                .events
 | 
			
		||||
                .back()
 | 
			
		||||
                .map_or(false, |ev| matches!(ev.kind, EventKind::Str))
 | 
			
		||||
        {
 | 
			
		||||
            if let Some(ev) = self.parse_event() {
 | 
			
		||||
                self.events.push_back(ev);
 | 
			
		||||
                dbg!(&self.events, &self.state);
 | 
			
		||||
            } else {
 | 
			
		||||
                need_more = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.last || !need_more {
 | 
			
		||||
            self.events
 | 
			
		||||
                .pop_front()
 | 
			
		||||
                .map(|e| {
 | 
			
		||||
| 
						 | 
				
			
			@ -333,18 +366,17 @@ impl<'s> Iterator for Parser<'s> {
 | 
			
		|||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .or_else(|| {
 | 
			
		||||
                if self.last {
 | 
			
		||||
                    self.state.verbatim().map(|(kind, _)| {
 | 
			
		||||
                    self.state.verbatim().map(|(kind, _, _)| {
 | 
			
		||||
                        self.state = State::None;
 | 
			
		||||
                        Event {
 | 
			
		||||
                            kind: EventKind::Exit(kind),
 | 
			
		||||
                            span: self.span,
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								src/lex.rs
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								src/lex.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -83,15 +83,20 @@ impl Sequence {
 | 
			
		|||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub(crate) struct Lexer<'s> {
 | 
			
		||||
    pub src: &'s str,
 | 
			
		||||
    chars: std::str::Chars<'s>,
 | 
			
		||||
    /// Next character should be escaped.
 | 
			
		||||
    escape: bool,
 | 
			
		||||
    /// Token to be peeked or next'ed.
 | 
			
		||||
    next: Option<Token>,
 | 
			
		||||
    /// Length of current token.
 | 
			
		||||
    len: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s> Lexer<'s> {
 | 
			
		||||
    pub fn new(src: &'s str) -> Lexer<'s> {
 | 
			
		||||
        Lexer {
 | 
			
		||||
            src,
 | 
			
		||||
            chars: src.chars(),
 | 
			
		||||
            escape: false,
 | 
			
		||||
            next: None,
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +111,16 @@ impl<'s> Lexer<'s> {
 | 
			
		|||
        self.next.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn pos(&self) -> usize {
 | 
			
		||||
        self.src.len()
 | 
			
		||||
            - self.chars.as_str().len()
 | 
			
		||||
            - self.next.as_ref().map(|t| t.len).unwrap_or_default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn peek_ahead(&mut self) -> &'s str {
 | 
			
		||||
        &self.src[self.pos()..]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn next_token(&mut self) -> Option<Token> {
 | 
			
		||||
        let mut current = self.token();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								src/lib.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -234,7 +234,7 @@ impl<'s> Event<'s> {
 | 
			
		|||
                    inline::Container::Verbatim => Container::Verbatim,
 | 
			
		||||
                    inline::Container::InlineMath => Container::Math { display: false },
 | 
			
		||||
                    inline::Container::DisplayMath => Container::Math { display: true },
 | 
			
		||||
                    inline::Container::RawFormat => Container::RawInline { format: todo!() },
 | 
			
		||||
                    inline::Container::RawFormat => Container::RawInline { format: content },
 | 
			
		||||
                    inline::Container::Subscript => Container::Subscript,
 | 
			
		||||
                    inline::Container::Superscript => Container::Superscript,
 | 
			
		||||
                    inline::Container::Insert => Container::Insert,
 | 
			
		||||
| 
						 | 
				
			
			@ -483,10 +483,21 @@ mod test {
 | 
			
		|||
            "`abc\ndef",
 | 
			
		||||
            Start(Paragraph, Attributes::none()),
 | 
			
		||||
            Start(Verbatim, Attributes::none()),
 | 
			
		||||
            Str("abc\n"),
 | 
			
		||||
            Str("def"),
 | 
			
		||||
            Str("abc\ndef"),
 | 
			
		||||
            End(Verbatim),
 | 
			
		||||
            End(Paragraph),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn raw_inline() {
 | 
			
		||||
        test_parse!(
 | 
			
		||||
            "`raw\nraw`{=format}",
 | 
			
		||||
            Start(Paragraph, Attributes::none()),
 | 
			
		||||
            Start(RawInline { format: "format" }, Attributes::none()),
 | 
			
		||||
            Str("raw\nraw"),
 | 
			
		||||
            End(RawInline { format: "format" }),
 | 
			
		||||
            End(Paragraph),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue