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())
|
.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,7 +162,8 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
Container::DoubleQuoted => self.out.write_str("”")?,
|
Container::DoubleQuoted => self.out.write_str("”")?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Str(mut s) => {
|
Event::Str(mut s) => match self.raw {
|
||||||
|
Raw::None => {
|
||||||
let mut ent = "";
|
let mut ent = "";
|
||||||
while let Some(i) = s.chars().position(|c| {
|
while let Some(i) = s.chars().position(|c| {
|
||||||
if let Some(s) = match 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)?;
|
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("…")?,
|
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
|
/// 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("{=") {
|
||||||
|
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 {
|
} else {
|
||||||
EventKind::Exit(kind)
|
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,20 +323,25 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.last || !need_more {
|
||||||
self.events
|
self.events
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
|
@ -333,18 +366,17 @@ impl<'s> Iterator for Parser<'s> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if self.last {
|
self.state.verbatim().map(|(kind, _, _)| {
|
||||||
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/lex.rs
15
src/lex.rs
|
@ -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();
|
||||||
|
|
||||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue