raw inline format

This commit is contained in:
Noah Hellman 2022-12-11 10:45:05 +01:00
parent 72bedb53b4
commit e798dc9c28
4 changed files with 154 additions and 73 deletions

View file

@ -42,14 +42,25 @@ pub fn write<'s, I: Iterator<Item = Event<'s>>, W: std::io::Write>(
.map_err(|_| output.error.unwrap_err()) .map_err(|_| output.error.unwrap_err())
} }
enum Raw {
None,
Html,
Other,
}
struct Writer<I, W> { struct Writer<I, W> {
events: I, events: I,
out: W, out: W,
raw: Raw,
} }
impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> { impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
fn new(events: I, out: W) -> Self { fn new(events: I, out: W) -> Self {
Self { events, out } Self {
events,
out,
raw: Raw::None,
}
} }
fn write(&mut self) -> std::fmt::Result { 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::Heading { level } => write!(self.out, "<h{}>", level)?,
Container::TableCell => self.out.write_str("<td>")?, Container::TableCell => self.out.write_str("<td>")?,
Container::DescriptionTerm => self.out.write_str("<dt>")?, Container::DescriptionTerm => self.out.write_str("<dt>")?,
Container::RawBlock { .. } => todo!(),
Container::CodeBlock { lang } => { Container::CodeBlock { lang } => {
if let Some(l) = lang { if let Some(l) = lang {
write!(self.out, r#"<pre><code class="language-{}">"#, l)?; 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 { } else {
r#"<span class="math inline">\("# 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::Subscript => self.out.write_str("<sub>")?,
Container::Superscript => self.out.write_str("<sup>")?, Container::Superscript => self.out.write_str("<sup>")?,
Container::Insert => self.out.write_str("<ins>")?, 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::Heading { level } => write!(self.out, "</h{}>", level)?,
Container::TableCell => self.out.write_str("</td>")?, Container::TableCell => self.out.write_str("</td>")?,
Container::DescriptionTerm => self.out.write_str("</dt>")?, Container::DescriptionTerm => self.out.write_str("</dt>")?,
Container::RawBlock { .. } => todo!(),
Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?, Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
Container::Span => self.out.write_str("</span>")?, Container::Span => self.out.write_str("</span>")?,
Container::Link(..) => todo!(), Container::Link(..) => todo!(),
Container::Image(..) => todo!(), Container::Image(..) => todo!(),
Container::Verbatim => self.out.write_str("</code>")?, Container::Verbatim => self.out.write_str("</code>")?,
Container::Math { .. } => self.out.write_str("</span>")?, 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::Subscript => self.out.write_str("</sub>")?,
Container::Superscript => self.out.write_str("</sup>")?, Container::Superscript => self.out.write_str("</sup>")?,
Container::Insert => self.out.write_str("</ins>")?, Container::Insert => self.out.write_str("</ins>")?,
@ -145,28 +162,34 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
Container::DoubleQuoted => self.out.write_str("&rdquo;")?, Container::DoubleQuoted => self.out.write_str("&rdquo;")?,
} }
} }
Event::Str(mut s) => { Event::Str(mut s) => match self.raw {
let mut ent = ""; Raw::None => {
while let Some(i) = s.chars().position(|c| { let mut ent = "";
if let Some(s) = match c { while let Some(i) = s.chars().position(|c| {
'<' => Some("&lt;"), if let Some(s) = match c {
'>' => Some("&gt;"), '<' => Some("&lt;"),
'&' => Some("&amp;"), '>' => Some("&gt;"),
'"' => Some("&quot;"), '&' => Some("&amp;"),
_ => None, '"' => Some("&quot;"),
} { _ => None,
ent = s; } {
true ent = s;
} else { true
false } else {
false
}
}) {
self.out.write_str(&s[..i])?;
self.out.write_str(ent)?;
s = &s[i + 1..];
} }
}) { self.out.write_str(s)?;
self.out.write_str(&s[..i])?;
self.out.write_str(ent)?;
s = &s[i + 1..];
} }
self.out.write_str(s)?; Raw::Html => {
} self.out.write_str(s)?;
}
Raw::Other => {}
},
Event::Atom(a) => match a { Event::Atom(a) => match a {
Atom::Ellipsis => self.out.write_str("&hellip;")?, Atom::Ellipsis => self.out.write_str("&hellip;")?,

View file

@ -60,12 +60,14 @@ pub struct Event {
/// Current parsing state of elements that are not recursive, i.e. may not contain arbitrary inline /// 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. /// elements, can only be one of these at a time.
#[derive(Debug)]
enum State { enum State {
None, None,
/// Within a verbatim element, e.g. '$`xxxxx' /// Within a verbatim element, e.g. '$`xxxxx'
Verbatim { Verbatim {
kind: Container, kind: Container,
opener_len: usize, opener_len: usize,
opener_event: usize,
}, },
/// Potentially within an attribute list, e.g. '{a=b '. /// Potentially within an attribute list, e.g. '{a=b '.
Attributes { Attributes {
@ -81,9 +83,14 @@ enum State {
} }
impl State { impl State {
fn verbatim(&self) -> Option<(Container, usize)> { fn verbatim(&self) -> Option<(Container, usize, usize)> {
if let Self::Verbatim { kind, opener_len } = self { if let Self::Verbatim {
Some((*kind, *opener_len)) kind,
opener_len,
opener_event,
} = self
{
Some((*kind, *opener_len, *opener_event))
} else { } else {
None None
} }
@ -173,16 +180,34 @@ impl<'s> Parser<'s> {
fn parse_verbatim(&mut self, first: &lex::Token) -> Option<Event> { fn parse_verbatim(&mut self, first: &lex::Token) -> Option<Event> {
self.state self.state
.verbatim() .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)) let kind = if matches!(first.kind, lex::Kind::Seq(lex::Sequence::Backtick))
&& first.len == opener_len && first.len == opener_len
{ {
self.state = State::None; self.state = State::None;
if matches!(kind, Container::Span) { let kind =
todo!() if matches!(kind, Verbatim) && self.lexer.peek_ahead().starts_with("{=") {
} else { let mut chars = self.lexer.peek_ahead()[2..].chars();
EventKind::Exit(kind) 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 {
Verbatim
}
} else {
kind
};
EventKind::Exit(kind)
} else { } else {
EventKind::Str EventKind::Str
}; };
@ -203,9 +228,9 @@ impl<'s> Parser<'s> {
{ {
Some(( Some((
if first.len == 2 { if first.len == 2 {
Container::DisplayMath DisplayMath
} else { } else {
Container::InlineMath InlineMath
}, },
*len, *len,
)) ))
@ -219,13 +244,16 @@ impl<'s> Parser<'s> {
} }
math_opt math_opt
} }
lex::Kind::Seq(lex::Sequence::Backtick) => { lex::Kind::Seq(lex::Sequence::Backtick) => Some((Verbatim, first.len)),
Some((Container::Verbatim, first.len))
}
_ => None, _ => None,
} }
.map(|(kind, opener_len)| { .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 { Event {
kind: EventKind::Enter(kind), kind: EventKind::Enter(kind),
span: self.span, span: self.span,
@ -295,56 +323,60 @@ 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> {
let mut need_more = false;
while self.events.is_empty() while self.events.is_empty()
|| !self.openers.is_empty() || !self.openers.is_empty()
|| self || !matches!(self.state, State::None)
|| self // for merge
.events .events
.back() .back()
.map_or(false, |ev| matches!(ev.kind, EventKind::Str)) .map_or(false, |ev| matches!(ev.kind, EventKind::Str))
{ {
if let Some(ev) = self.parse_event() { if let Some(ev) = self.parse_event() {
self.events.push_back(ev); self.events.push_back(ev);
dbg!(&self.events, &self.state);
} else { } else {
need_more = true;
break; break;
} }
} }
self.events if self.last || !need_more {
.pop_front() self.events
.map(|e| { .pop_front()
if matches!(e.kind, EventKind::Str) { .map(|e| {
// merge str events if matches!(e.kind, EventKind::Str) {
let mut span = e.span; // merge str events
while self let mut span = e.span;
.events while self
.front() .events
.map_or(false, |ev| matches!(ev.kind, EventKind::Str)) .front()
{ .map_or(false, |ev| matches!(ev.kind, EventKind::Str))
let ev = self.events.pop_front().unwrap(); {
assert_eq!(span.end(), ev.span.start()); let ev = self.events.pop_front().unwrap();
span = span.union(ev.span); assert_eq!(span.end(), ev.span.start());
span = span.union(ev.span);
}
Event {
kind: EventKind::Str,
span,
}
} else {
e
} }
Event { })
kind: EventKind::Str, .or_else(|| {
span, self.state.verbatim().map(|(kind, _, _)| {
}
} else {
e
}
})
.or_else(|| {
if self.last {
self.state.verbatim().map(|(kind, _)| {
self.state = State::None; self.state = State::None;
Event { Event {
kind: EventKind::Exit(kind), kind: EventKind::Exit(kind),
span: self.span, span: self.span,
} }
}) })
} else { })
None } else {
} None
}) }
} }
} }

View file

@ -83,15 +83,20 @@ impl Sequence {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct Lexer<'s> { pub(crate) struct Lexer<'s> {
pub src: &'s str,
chars: std::str::Chars<'s>, chars: std::str::Chars<'s>,
/// Next character should be escaped.
escape: bool, escape: bool,
/// Token to be peeked or next'ed.
next: Option<Token>, next: Option<Token>,
/// Length of current token.
len: usize, len: usize,
} }
impl<'s> Lexer<'s> { impl<'s> Lexer<'s> {
pub fn new(src: &'s str) -> Lexer<'s> { pub fn new(src: &'s str) -> Lexer<'s> {
Lexer { Lexer {
src,
chars: src.chars(), chars: src.chars(),
escape: false, escape: false,
next: None, next: None,
@ -106,6 +111,16 @@ impl<'s> Lexer<'s> {
self.next.as_ref() 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> { fn next_token(&mut self) -> Option<Token> {
let mut current = self.token(); let mut current = self.token();

View file

@ -234,7 +234,7 @@ impl<'s> Event<'s> {
inline::Container::Verbatim => Container::Verbatim, inline::Container::Verbatim => Container::Verbatim,
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 { format: todo!() }, inline::Container::RawFormat => Container::RawInline { format: content },
inline::Container::Subscript => Container::Subscript, inline::Container::Subscript => Container::Subscript,
inline::Container::Superscript => Container::Superscript, inline::Container::Superscript => Container::Superscript,
inline::Container::Insert => Container::Insert, inline::Container::Insert => Container::Insert,
@ -483,10 +483,21 @@ mod test {
"`abc\ndef", "`abc\ndef",
Start(Paragraph, Attributes::none()), Start(Paragraph, Attributes::none()),
Start(Verbatim, Attributes::none()), Start(Verbatim, Attributes::none()),
Str("abc\n"), Str("abc\ndef"),
Str("def"),
End(Verbatim), End(Verbatim),
End(Paragraph), 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),
);
}
} }