verbatim fix
This commit is contained in:
parent
a994228bb5
commit
2303cf3574
5 changed files with 278 additions and 174 deletions
|
@ -309,7 +309,8 @@ impl Block {
|
||||||
f @ ('`' | ':' | '~') => {
|
f @ ('`' | ':' | '~') => {
|
||||||
let fence_length = (&mut chars).take_while(|c| *c == f).count() + 1;
|
let fence_length = (&mut chars).take_while(|c| *c == f).count() + 1;
|
||||||
let lang = line_t[fence_length..].trim();
|
let lang = line_t[fence_length..].trim();
|
||||||
let valid_spec = !lang.chars().any(char::is_whitespace);
|
let valid_spec =
|
||||||
|
!lang.chars().any(char::is_whitespace) && !lang.chars().any(|c| c == '`');
|
||||||
(valid_spec && fence_length >= 3)
|
(valid_spec && fence_length >= 3)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
u8::try_from(fence_length).ok().map(|fence_length| {
|
u8::try_from(fence_length).ok().map(|fence_length| {
|
||||||
|
|
34
src/html.rs
34
src/html.rs
|
@ -75,11 +75,8 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
self.out.write_str("<div>")?;
|
self.out.write_str("<div>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::Span => self.out.write_str("<span>")?,
|
|
||||||
Container::Paragraph => self.out.write_str("<p>")?,
|
Container::Paragraph => self.out.write_str("<p>")?,
|
||||||
Container::Heading { level } => write!(self.out, "<h{}>", level)?,
|
Container::Heading { level } => write!(self.out, "<h{}>", level)?,
|
||||||
Container::Link(..) => todo!(),
|
|
||||||
Container::Image(..) => todo!(),
|
|
||||||
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::RawBlock { .. } => todo!(),
|
||||||
|
@ -90,6 +87,16 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
self.out.write_str("<pre><code>")?;
|
self.out.write_str("<pre><code>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Container::Span => self.out.write_str("<span>")?,
|
||||||
|
Container::Link(..) => todo!(),
|
||||||
|
Container::Image(..) => todo!(),
|
||||||
|
Container::Verbatim => self.out.write_str("<code>")?,
|
||||||
|
Container::Math { display } => self.out.write_str(if display {
|
||||||
|
r#"<span class="math display">\["#
|
||||||
|
} else {
|
||||||
|
r#"<span class="math inline">\("#
|
||||||
|
})?,
|
||||||
|
Container::RawInline { .. } => todo!(),
|
||||||
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>")?,
|
||||||
|
@ -119,11 +126,14 @@ 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 { .. } => self.out.write_str("</code></pre>")?,
|
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::Math { .. } => self.out.write_str("</span>")?,
|
||||||
|
Container::RawInline { .. } => todo!(),
|
||||||
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>")?,
|
||||||
|
@ -136,22 +146,6 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Str(s) => self.out.write_str(s)?,
|
Event::Str(s) => self.out.write_str(s)?,
|
||||||
Event::Verbatim(s) => write!(self.out, "<code>{}</code>", s)?,
|
|
||||||
Event::Math { content, display } => {
|
|
||||||
if display {
|
|
||||||
write!(
|
|
||||||
self.out,
|
|
||||||
r#"<span class="math display">\[{}\]</span>"#,
|
|
||||||
content,
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
self.out,
|
|
||||||
r#"<span class="math inline">\({}\)</span>"#,
|
|
||||||
content,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Atom(a) => match a {
|
Event::Atom(a) => match a {
|
||||||
Atom::Ellipsis => self.out.write_str("…")?,
|
Atom::Ellipsis => self.out.write_str("…")?,
|
||||||
Atom::EnDash => self.out.write_str("–")?,
|
Atom::EnDash => self.out.write_str("–")?,
|
||||||
|
|
319
src/inline.rs
319
src/inline.rs
|
@ -6,7 +6,6 @@ use lex::Symbol;
|
||||||
|
|
||||||
use Atom::*;
|
use Atom::*;
|
||||||
use Container::*;
|
use Container::*;
|
||||||
use Node::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Atom {
|
pub enum Atom {
|
||||||
|
@ -17,24 +16,6 @@ pub enum Atom {
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
EnDash,
|
EnDash,
|
||||||
EmDash,
|
EmDash,
|
||||||
Lt,
|
|
||||||
Gt,
|
|
||||||
Ampersand,
|
|
||||||
Quote,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Node {
|
|
||||||
Str,
|
|
||||||
// link
|
|
||||||
//Url,
|
|
||||||
//ImageSource,
|
|
||||||
//LinkReference,
|
|
||||||
//FootnoteReference,
|
|
||||||
Verbatim,
|
|
||||||
RawFormat { format: Span },
|
|
||||||
InlineMath,
|
|
||||||
DisplayMath,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
@ -52,6 +33,11 @@ pub enum Container {
|
||||||
// smart quoting
|
// smart quoting
|
||||||
SingleQuoted,
|
SingleQuoted,
|
||||||
DoubleQuoted,
|
DoubleQuoted,
|
||||||
|
// Verbatim
|
||||||
|
Verbatim,
|
||||||
|
RawFormat,
|
||||||
|
InlineMath,
|
||||||
|
DisplayMath,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -59,7 +45,7 @@ pub enum EventKind {
|
||||||
Enter(Container),
|
Enter(Container),
|
||||||
Exit(Container),
|
Exit(Container),
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
Node(Node),
|
Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -81,6 +67,9 @@ pub struct Parser<'s> {
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
||||||
lexer: std::iter::Peekable<lex::Lexer<'s>>,
|
lexer: std::iter::Peekable<lex::Lexer<'s>>,
|
||||||
|
|
||||||
|
verbatim: Option<(Container, usize)>,
|
||||||
|
last: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Parser<'s> {
|
impl<'s> Parser<'s> {
|
||||||
|
@ -91,11 +80,18 @@ impl<'s> Parser<'s> {
|
||||||
span: Span::new(0, 0),
|
span: Span::new(0, 0),
|
||||||
|
|
||||||
lexer: lex::Lexer::new("").peekable(),
|
lexer: lex::Lexer::new("").peekable(),
|
||||||
|
|
||||||
|
verbatim: None,
|
||||||
|
last: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self, src: &'s str) {
|
pub fn parse(&mut self, src: &'s str, last: bool) {
|
||||||
self.lexer = lex::Lexer::new(src).peekable();
|
self.lexer = lex::Lexer::new(src).peekable();
|
||||||
|
if last {
|
||||||
|
assert!(!self.last);
|
||||||
|
}
|
||||||
|
self.last = last;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat(&mut self) -> Option<lex::Token> {
|
fn eat(&mut self) -> Option<lex::Token> {
|
||||||
|
@ -114,20 +110,16 @@ impl<'s> Parser<'s> {
|
||||||
self.span = Span::empty_at(self.span.end());
|
self.span = Span::empty_at(self.span.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node(&self, kind: Node) -> Event {
|
|
||||||
Event {
|
|
||||||
kind: EventKind::Node(kind),
|
|
||||||
span: self.span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_event(&mut self) -> Option<Event> {
|
fn parse_event(&mut self) -> Option<Event> {
|
||||||
self.reset_span();
|
self.reset_span();
|
||||||
self.eat().map(|first| {
|
self.eat().map(|first| {
|
||||||
self.parse_verbatim(&first)
|
self.parse_verbatim(&first)
|
||||||
.or_else(|| self.parse_container(&first))
|
.or_else(|| self.parse_container(&first))
|
||||||
.or_else(|| self.parse_atom(&first))
|
.or_else(|| self.parse_atom(&first))
|
||||||
.unwrap_or_else(|| self.node(Str))
|
.unwrap_or(Event {
|
||||||
|
kind: EventKind::Str,
|
||||||
|
span: self.span,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +130,6 @@ impl<'s> Parser<'s> {
|
||||||
lex::Kind::Seq(lex::Sequence::Period) if first.len == 3 => Ellipsis,
|
lex::Kind::Seq(lex::Sequence::Period) if first.len == 3 => Ellipsis,
|
||||||
lex::Kind::Seq(lex::Sequence::Hyphen) if first.len == 2 => EnDash,
|
lex::Kind::Seq(lex::Sequence::Hyphen) if first.len == 2 => EnDash,
|
||||||
lex::Kind::Seq(lex::Sequence::Hyphen) if first.len == 3 => EmDash,
|
lex::Kind::Seq(lex::Sequence::Hyphen) if first.len == 3 => EmDash,
|
||||||
lex::Kind::Sym(lex::Symbol::Lt) => Lt,
|
|
||||||
lex::Kind::Sym(lex::Symbol::Gt) => Gt,
|
|
||||||
lex::Kind::Sym(lex::Symbol::Quote2) => Quote,
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,51 +140,62 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_verbatim(&mut self, first: &lex::Token) -> Option<Event> {
|
fn parse_verbatim(&mut self, first: &lex::Token) -> Option<Event> {
|
||||||
match first.kind {
|
self.verbatim
|
||||||
lex::Kind::Seq(lex::Sequence::Dollar) => {
|
.map(|(kind, opener_len)| {
|
||||||
let math_opt = (first.len <= 2)
|
let kind = if matches!(first.kind, lex::Kind::Seq(lex::Sequence::Backtick))
|
||||||
.then(|| {
|
&& first.len == opener_len
|
||||||
if let Some(lex::Token {
|
|
||||||
kind: lex::Kind::Seq(lex::Sequence::Backtick),
|
|
||||||
len,
|
|
||||||
}) = self.peek()
|
|
||||||
{
|
|
||||||
Some((
|
|
||||||
if first.len == 2 {
|
|
||||||
DisplayMath
|
|
||||||
} else {
|
|
||||||
InlineMath
|
|
||||||
},
|
|
||||||
*len,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten();
|
|
||||||
if math_opt.is_some() {
|
|
||||||
self.eat(); // backticks
|
|
||||||
}
|
|
||||||
math_opt
|
|
||||||
}
|
|
||||||
lex::Kind::Seq(lex::Sequence::Backtick) => Some((Verbatim, first.len)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
.map(|(kind, opener_len)| {
|
|
||||||
let mut span = Span::empty_at(self.span.end());
|
|
||||||
while let Some(tok) = self.eat() {
|
|
||||||
if matches!(tok.kind, lex::Kind::Seq(lex::Sequence::Backtick))
|
|
||||||
&& tok.len == opener_len
|
|
||||||
{
|
{
|
||||||
break;
|
self.verbatim = None;
|
||||||
|
EventKind::Exit(kind)
|
||||||
|
} else {
|
||||||
|
EventKind::Str
|
||||||
|
};
|
||||||
|
Event {
|
||||||
|
kind,
|
||||||
|
span: self.span,
|
||||||
}
|
}
|
||||||
span = span.extend(tok.len);
|
})
|
||||||
}
|
.or_else(|| {
|
||||||
Event {
|
match first.kind {
|
||||||
kind: EventKind::Node(kind),
|
lex::Kind::Seq(lex::Sequence::Dollar) => {
|
||||||
span,
|
let math_opt = (first.len <= 2)
|
||||||
}
|
.then(|| {
|
||||||
})
|
if let Some(lex::Token {
|
||||||
|
kind: lex::Kind::Seq(lex::Sequence::Backtick),
|
||||||
|
len,
|
||||||
|
}) = self.peek()
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
if first.len == 2 {
|
||||||
|
Container::DisplayMath
|
||||||
|
} else {
|
||||||
|
Container::InlineMath
|
||||||
|
},
|
||||||
|
*len,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
if math_opt.is_some() {
|
||||||
|
self.eat(); // backticks
|
||||||
|
}
|
||||||
|
math_opt
|
||||||
|
}
|
||||||
|
lex::Kind::Seq(lex::Sequence::Backtick) => {
|
||||||
|
Some((Container::Verbatim, first.len))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
.map(|(kind, opener_len)| {
|
||||||
|
self.verbatim = Some((kind, opener_len));
|
||||||
|
Event {
|
||||||
|
kind: EventKind::Enter(kind),
|
||||||
|
span: self.span,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_container(&mut self, first: &lex::Token) -> Option<Event> {
|
fn parse_container(&mut self, first: &lex::Token) -> Option<Event> {
|
||||||
|
@ -239,7 +239,7 @@ impl<'s> Parser<'s> {
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.openers.push((cont, self.events.len()));
|
self.openers.push((cont, self.events.len()));
|
||||||
// use str for now, replace if closed later
|
// use str for now, replace if closed later
|
||||||
EventKind::Node(Str)
|
EventKind::Str
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|kind| Event {
|
.map(|kind| Event {
|
||||||
|
@ -258,7 +258,7 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
|| self
|
|| self
|
||||||
.events
|
.events
|
||||||
.back()
|
.back()
|
||||||
.map_or(false, |ev| matches!(ev.kind, EventKind::Node(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);
|
||||||
|
@ -267,25 +267,39 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.events.pop_front().map(|e| {
|
self.events
|
||||||
if matches!(e.kind, EventKind::Node(Str)) {
|
.pop_front()
|
||||||
// merge str events
|
.map(|e| {
|
||||||
let mut span = e.span;
|
if matches!(e.kind, EventKind::Str) {
|
||||||
while self
|
// merge str events
|
||||||
.events
|
let mut span = e.span;
|
||||||
.front()
|
while self
|
||||||
.map_or(false, |ev| matches!(ev.kind, EventKind::Node(Str)))
|
.events
|
||||||
{
|
.front()
|
||||||
span = span.union(self.events.pop_front().unwrap().span);
|
.map_or(false, |ev| matches!(ev.kind, EventKind::Str))
|
||||||
|
{
|
||||||
|
let ev = self.events.pop_front().unwrap();
|
||||||
|
assert_eq!(span.end(), ev.span.start());
|
||||||
|
span = span.union(ev.span);
|
||||||
|
}
|
||||||
|
Event {
|
||||||
|
kind: EventKind::Str,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e
|
||||||
}
|
}
|
||||||
Event {
|
})
|
||||||
kind: EventKind::Node(Str),
|
.or_else(|| {
|
||||||
span,
|
if self.last {
|
||||||
|
self.verbatim.take().map(|(kind, _)| Event {
|
||||||
|
kind: EventKind::Exit(kind),
|
||||||
|
span: self.span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,49 +310,106 @@ mod test {
|
||||||
use super::Atom::*;
|
use super::Atom::*;
|
||||||
use super::Container::*;
|
use super::Container::*;
|
||||||
use super::EventKind::*;
|
use super::EventKind::*;
|
||||||
use super::Node::*;
|
use super::Verbatim;
|
||||||
|
|
||||||
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();
|
let mut p = super::Parser::new();
|
||||||
p.parse($src);
|
p.parse($src, 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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::EventKind {
|
|
||||||
pub fn span(self, start: usize, end: usize) -> super::Event {
|
|
||||||
super::Event {
|
|
||||||
span: Span::new(start, end),
|
|
||||||
kind: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn str() {
|
fn str() {
|
||||||
test_parse!("abc", (Node(Str), "abc"));
|
test_parse!("abc", (Str, "abc"));
|
||||||
test_parse!("abc def", (Node(Str), "abc def"));
|
test_parse!("abc def", (Str, "abc def"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verbatim() {
|
fn verbatim() {
|
||||||
test_parse!("`abc`", (Node(Verbatim), "abc"));
|
test_parse!(
|
||||||
test_parse!("`abc", (Node(Verbatim), "abc"));
|
"`abc`",
|
||||||
test_parse!("``abc``", (Node(Verbatim), "abc"));
|
(Enter(Verbatim), "`"),
|
||||||
test_parse!("abc `def`", (Node(Str), "abc "), (Node(Verbatim), "def"));
|
(Str, "abc"),
|
||||||
|
(Exit(Verbatim), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"`abc\ndef`",
|
||||||
|
(Enter(Verbatim), "`"),
|
||||||
|
(Str, "abc\ndef"),
|
||||||
|
(Exit(Verbatim), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"`abc&def`",
|
||||||
|
(Enter(Verbatim), "`"),
|
||||||
|
(Str, "abc&def"),
|
||||||
|
(Exit(Verbatim), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"`abc",
|
||||||
|
(Enter(Verbatim), "`"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(Verbatim), ""),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"``abc``",
|
||||||
|
(Enter(Verbatim), "``"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(Verbatim), "``"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"abc `def`",
|
||||||
|
(Str, "abc "),
|
||||||
|
(Enter(Verbatim), "`"),
|
||||||
|
(Str, "def"),
|
||||||
|
(Exit(Verbatim), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"abc`def`",
|
||||||
|
(Str, "abc"),
|
||||||
|
(Enter(Verbatim), "`"),
|
||||||
|
(Str, "def"),
|
||||||
|
(Exit(Verbatim), "`"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn math() {
|
fn math() {
|
||||||
test_parse!("$`abc`", (Node(InlineMath), "abc"));
|
test_parse!(
|
||||||
test_parse!("$`abc` str", (Node(InlineMath), "abc"), (Node(Str), " str"));
|
"$`abc`",
|
||||||
test_parse!("$$`abc`", (Node(DisplayMath), "abc"));
|
(Enter(InlineMath), "$`"),
|
||||||
test_parse!("$`abc", (Node(InlineMath), "abc"));
|
(Str, "abc"),
|
||||||
test_parse!("$```abc```", (Node(InlineMath), "abc"),);
|
(Exit(InlineMath), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"$`abc` str",
|
||||||
|
(Enter(InlineMath), "$`"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(InlineMath), "`"),
|
||||||
|
(Str, " str"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"$$`abc`",
|
||||||
|
(Enter(DisplayMath), "$$`"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(DisplayMath), "`"),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"$`abc",
|
||||||
|
(Enter(InlineMath), "$`"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(InlineMath), ""),
|
||||||
|
);
|
||||||
|
test_parse!(
|
||||||
|
"$```abc```",
|
||||||
|
(Enter(InlineMath), "$```"),
|
||||||
|
(Str, "abc"),
|
||||||
|
(Exit(InlineMath), "```"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -346,13 +417,13 @@ mod test {
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"_abc_",
|
"_abc_",
|
||||||
(Enter(Emphasis), "_"),
|
(Enter(Emphasis), "_"),
|
||||||
(Node(Str), "abc"),
|
(Str, "abc"),
|
||||||
(Exit(Emphasis), "_"),
|
(Exit(Emphasis), "_"),
|
||||||
);
|
);
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"{_abc_}",
|
"{_abc_}",
|
||||||
(Enter(Emphasis), "{_"),
|
(Enter(Emphasis), "{_"),
|
||||||
(Node(Str), "abc"),
|
(Str, "abc"),
|
||||||
(Exit(Emphasis), "_}"),
|
(Exit(Emphasis), "_}"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -363,7 +434,7 @@ mod test {
|
||||||
"{_{_abc_}_}",
|
"{_{_abc_}_}",
|
||||||
(Enter(Emphasis), "{_"),
|
(Enter(Emphasis), "{_"),
|
||||||
(Enter(Emphasis), "{_"),
|
(Enter(Emphasis), "{_"),
|
||||||
(Node(Str), "abc"),
|
(Str, "abc"),
|
||||||
(Exit(Emphasis), "_}"),
|
(Exit(Emphasis), "_}"),
|
||||||
(Exit(Emphasis), "_}"),
|
(Exit(Emphasis), "_}"),
|
||||||
);
|
);
|
||||||
|
@ -371,7 +442,7 @@ mod test {
|
||||||
"*_abc_*",
|
"*_abc_*",
|
||||||
(Enter(Strong), "*"),
|
(Enter(Strong), "*"),
|
||||||
(Enter(Emphasis), "_"),
|
(Enter(Emphasis), "_"),
|
||||||
(Node(Str), "abc"),
|
(Str, "abc"),
|
||||||
(Exit(Emphasis), "_"),
|
(Exit(Emphasis), "_"),
|
||||||
(Exit(Strong), "*"),
|
(Exit(Strong), "*"),
|
||||||
);
|
);
|
||||||
|
@ -379,7 +450,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn container_unopened() {
|
fn container_unopened() {
|
||||||
test_parse!("*}abc", (Node(Str), "*}abc"));
|
test_parse!("*}abc", (Str, "*}abc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -387,14 +458,14 @@ mod test {
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"{*{_abc*}",
|
"{*{_abc*}",
|
||||||
(Enter(Strong), "{*"),
|
(Enter(Strong), "{*"),
|
||||||
(Node(Str), "{_abc"),
|
(Str, "{_abc"),
|
||||||
(Exit(Strong), "*}"),
|
(Exit(Strong), "*}"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn container_close_block() {
|
fn container_close_block() {
|
||||||
test_parse!("{_abc", (Node(Str), "{_abc"));
|
test_parse!("{_abc", (Str, "{_abc"));
|
||||||
test_parse!("{_{*{_abc", (Node(Str), "{_{*{_abc"));
|
test_parse!("{_{*{_abc", (Str, "{_{*{_abc"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
59
src/lib.rs
59
src/lib.rs
|
@ -22,10 +22,6 @@ pub enum Event<'s> {
|
||||||
Str(&'s str),
|
Str(&'s str),
|
||||||
/// An atomic element.
|
/// An atomic element.
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
/// A verbatim string.
|
|
||||||
Verbatim(&'s str),
|
|
||||||
/// An inline or display math element.
|
|
||||||
Math { content: &'s str, display: bool },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -66,6 +62,12 @@ pub enum Container<'s> {
|
||||||
Link(&'s str, LinkType),
|
Link(&'s str, LinkType),
|
||||||
/// An inline image.
|
/// An inline image.
|
||||||
Image(&'s str),
|
Image(&'s str),
|
||||||
|
/// An inline verbatim string.
|
||||||
|
Verbatim,
|
||||||
|
/// An inline or display math element.
|
||||||
|
Math { display: bool },
|
||||||
|
/// Inline raw markup for a specific output format.
|
||||||
|
RawInline { format: &'s str },
|
||||||
/// A subscripted element.
|
/// A subscripted element.
|
||||||
Subscript,
|
Subscript,
|
||||||
/// A superscripted element.
|
/// A superscripted element.
|
||||||
|
@ -108,6 +110,9 @@ impl<'s> Container<'s> {
|
||||||
Self::Span
|
Self::Span
|
||||||
| Self::Link(..)
|
| Self::Link(..)
|
||||||
| Self::Image(..)
|
| Self::Image(..)
|
||||||
|
| Self::Verbatim
|
||||||
|
| Self::Math { .. }
|
||||||
|
| Self::RawInline { .. }
|
||||||
| Self::Subscript
|
| Self::Subscript
|
||||||
| Self::Superscript
|
| Self::Superscript
|
||||||
| Self::Insert
|
| Self::Insert
|
||||||
|
@ -141,6 +146,9 @@ impl<'s> Container<'s> {
|
||||||
| Self::Span
|
| Self::Span
|
||||||
| Self::Link(..)
|
| Self::Link(..)
|
||||||
| Self::Image(..)
|
| Self::Image(..)
|
||||||
|
| Self::Verbatim
|
||||||
|
| Self::Math { .. }
|
||||||
|
| Self::RawInline { .. }
|
||||||
| Self::Subscript
|
| Self::Subscript
|
||||||
| Self::Superscript
|
| Self::Superscript
|
||||||
| Self::Insert
|
| Self::Insert
|
||||||
|
@ -223,6 +231,10 @@ impl<'s> Event<'s> {
|
||||||
inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
|
inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
|
||||||
let t = match c {
|
let t = match c {
|
||||||
inline::Container::Span => Container::Span,
|
inline::Container::Span => Container::Span,
|
||||||
|
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::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,
|
||||||
|
@ -248,21 +260,8 @@ impl<'s> Event<'s> {
|
||||||
inline::Atom::Softbreak => Atom::Softbreak,
|
inline::Atom::Softbreak => Atom::Softbreak,
|
||||||
inline::Atom::Hardbreak => Atom::Hardbreak,
|
inline::Atom::Hardbreak => Atom::Hardbreak,
|
||||||
inline::Atom::Escape => Atom::Escape,
|
inline::Atom::Escape => Atom::Escape,
|
||||||
_ => todo!(),
|
|
||||||
}),
|
}),
|
||||||
inline::EventKind::Node(n) => match n {
|
inline::EventKind::Str => Self::Str(content),
|
||||||
inline::Node::Str => Self::Str(content),
|
|
||||||
inline::Node::Verbatim => Self::Verbatim(content),
|
|
||||||
inline::Node::InlineMath => Self::Math {
|
|
||||||
content,
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
inline::Node::DisplayMath => Self::Math {
|
|
||||||
content,
|
|
||||||
display: true,
|
|
||||||
},
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +315,7 @@ pub struct Parser<'s> {
|
||||||
tree: block::Tree,
|
tree: block::Tree,
|
||||||
parser: Option<inline::Parser<'s>>,
|
parser: Option<inline::Parser<'s>>,
|
||||||
inline_start: usize,
|
inline_start: usize,
|
||||||
attributes: Attributes<'s>,
|
block_attributes: Attributes<'s>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Parser<'s> {
|
impl<'s> Parser<'s> {
|
||||||
|
@ -327,7 +326,7 @@ impl<'s> Parser<'s> {
|
||||||
tree: block::parse(src),
|
tree: block::parse(src),
|
||||||
parser: None,
|
parser: None,
|
||||||
inline_start: 0,
|
inline_start: 0,
|
||||||
attributes: Attributes::none(),
|
block_attributes: Attributes::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,7 +344,8 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
match ev.kind {
|
match ev.kind {
|
||||||
tree::EventKind::Element(atom) => {
|
tree::EventKind::Element(atom) => {
|
||||||
assert_eq!(atom, block::Atom::Inline);
|
assert_eq!(atom, block::Atom::Inline);
|
||||||
parser.parse(ev.span.of(self.src));
|
let last_inline = self.tree.neighbors().next().is_none();
|
||||||
|
parser.parse(ev.span.of(self.src), last_inline);
|
||||||
}
|
}
|
||||||
tree::EventKind::Exit(block) => {
|
tree::EventKind::Exit(block) => {
|
||||||
self.parser = None;
|
self.parser = None;
|
||||||
|
@ -363,7 +363,7 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
block::Atom::Inline => panic!("inline outside leaf block"),
|
block::Atom::Inline => panic!("inline outside leaf block"),
|
||||||
block::Atom::Blankline => Event::Atom(Atom::Blankline),
|
block::Atom::Blankline => Event::Atom(Atom::Blankline),
|
||||||
block::Atom::Attributes => {
|
block::Atom::Attributes => {
|
||||||
self.attributes.parse(content);
|
self.block_attributes.parse(content);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -384,7 +384,7 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
},
|
},
|
||||||
b => Container::from_block(self.src, b),
|
b => Container::from_block(self.src, b),
|
||||||
};
|
};
|
||||||
Event::Start(container, self.attributes.take())
|
Event::Start(container, self.block_attributes.take())
|
||||||
}
|
}
|
||||||
tree::EventKind::Exit(block) => Event::End(Container::from_block(self.src, block)),
|
tree::EventKind::Exit(block) => Event::End(Container::from_block(self.src, block)),
|
||||||
};
|
};
|
||||||
|
@ -470,4 +470,17 @@ mod test {
|
||||||
End(Paragraph),
|
End(Paragraph),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verbatim() {
|
||||||
|
test_parse!(
|
||||||
|
"`abc\ndef",
|
||||||
|
Start(Paragraph, Attributes::none()),
|
||||||
|
Start(Verbatim, Attributes::none()),
|
||||||
|
Str("abc\n"),
|
||||||
|
Str("def"),
|
||||||
|
End(Verbatim),
|
||||||
|
End(Paragraph),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/tree.rs
37
src/tree.rs
|
@ -13,6 +13,16 @@ pub struct Event<C, A> {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Object<C, E> {
|
||||||
|
kind: ObjectKind<C, E>,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ObjectKind<C, E> {
|
||||||
|
Container(C),
|
||||||
|
Element(E),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Tree<C, E> {
|
pub struct Tree<C, E> {
|
||||||
nodes: Vec<Node<C, E>>,
|
nodes: Vec<Node<C, E>>,
|
||||||
|
@ -20,14 +30,32 @@ pub struct Tree<C, E> {
|
||||||
head: Option<NodeIndex>,
|
head: Option<NodeIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, E> Tree<C, E> {
|
impl<C: Copy, E: Copy> Tree<C, E> {
|
||||||
fn new(nodes: Vec<Node<C, E>>) -> Self {
|
fn new(nodes: Vec<Node<C, E>>) -> Self {
|
||||||
|
let head = nodes[NodeIndex::root().index()].next;
|
||||||
Self {
|
Self {
|
||||||
nodes,
|
nodes,
|
||||||
branch: Vec::new(),
|
branch: Vec::new(),
|
||||||
head: Some(NodeIndex::root()),
|
head,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn neighbors(&self) -> impl Iterator<Item = Object<C, E>> + '_ {
|
||||||
|
let mut head = self.head;
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
head.take().map(|h| {
|
||||||
|
let n = &self.nodes[h.index()];
|
||||||
|
let kind = match &n.kind {
|
||||||
|
NodeKind::Root => unreachable!(),
|
||||||
|
NodeKind::Container(c, _) => ObjectKind::Container(*c),
|
||||||
|
NodeKind::Element(e) => ObjectKind::Element(*e),
|
||||||
|
};
|
||||||
|
let span = n.span;
|
||||||
|
head = n.next;
|
||||||
|
Object { kind, span }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Copy, E: Copy> Iterator for Tree<C, E> {
|
impl<C: Copy, E: Copy> Iterator for Tree<C, E> {
|
||||||
|
@ -37,10 +65,7 @@ impl<C: Copy, E: Copy> Iterator for Tree<C, E> {
|
||||||
if let Some(head) = self.head {
|
if let Some(head) = self.head {
|
||||||
let n = &self.nodes[head.index()];
|
let n = &self.nodes[head.index()];
|
||||||
let kind = match &n.kind {
|
let kind = match &n.kind {
|
||||||
NodeKind::Root => {
|
NodeKind::Root => unreachable!(),
|
||||||
self.head = n.next;
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
NodeKind::Container(c, child) => {
|
NodeKind::Container(c, child) => {
|
||||||
self.branch.push(head);
|
self.branch.push(head);
|
||||||
self.head = *child;
|
self.head = *child;
|
||||||
|
|
Loading…
Reference in a new issue