img
This commit is contained in:
parent
cd54416902
commit
5ac05d1919
4 changed files with 85 additions and 29 deletions
33
src/html.rs
33
src/html.rs
|
@ -52,6 +52,7 @@ struct Writer<I, W> {
|
||||||
events: I,
|
events: I,
|
||||||
out: W,
|
out: W,
|
||||||
raw: Raw,
|
raw: Raw,
|
||||||
|
text_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
||||||
|
@ -60,6 +61,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
events,
|
events,
|
||||||
out,
|
out,
|
||||||
raw: Raw::None,
|
raw: Raw::None,
|
||||||
|
text_only: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +72,9 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
if c.is_block() {
|
if c.is_block() {
|
||||||
self.out.write_char('\n')?;
|
self.out.write_char('\n')?;
|
||||||
}
|
}
|
||||||
|
if self.text_only && !matches!(c, Container::Image(..)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match c {
|
match c {
|
||||||
Container::Blockquote => self.out.write_str("<blockquote>")?,
|
Container::Blockquote => self.out.write_str("<blockquote>")?,
|
||||||
Container::List(..) => todo!(),
|
Container::List(..) => todo!(),
|
||||||
|
@ -98,8 +103,11 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::Span => self.out.write_str("<span>")?,
|
Container::Span => self.out.write_str("<span>")?,
|
||||||
Container::Link(..) => todo!(),
|
Container::Link(dst, ..) => write!(self.out, r#"<a href="{}">"#, dst)?,
|
||||||
Container::Image(..) => todo!(),
|
Container::Image(..) => {
|
||||||
|
self.text_only = true;
|
||||||
|
self.out.write_str("<img")?;
|
||||||
|
}
|
||||||
Container::Verbatim => self.out.write_str("<code>")?,
|
Container::Verbatim => self.out.write_str("<code>")?,
|
||||||
Container::Math { display } => self.out.write_str(if display {
|
Container::Math { display } => self.out.write_str(if display {
|
||||||
r#"<span class="math display">\["#
|
r#"<span class="math display">\["#
|
||||||
|
@ -128,6 +136,9 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
|
if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
|
||||||
self.out.write_char('\n')?;
|
self.out.write_char('\n')?;
|
||||||
}
|
}
|
||||||
|
if self.text_only && !matches!(c, Container::Image(..)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match c {
|
match c {
|
||||||
Container::Blockquote => self.out.write_str("</blockquote>")?,
|
Container::Blockquote => self.out.write_str("</blockquote>")?,
|
||||||
Container::List(..) => todo!(),
|
Container::List(..) => todo!(),
|
||||||
|
@ -143,13 +154,21 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<I, W> {
|
||||||
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::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
|
Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
|
||||||
Container::Span => self.out.write_str("</span>")?,
|
Container::Span | Container::Math { .. } => {
|
||||||
Container::Link(..) => todo!(),
|
self.out.write_str("</span>")?;
|
||||||
Container::Image(..) => todo!(),
|
}
|
||||||
|
Container::Link(..) => self.out.write_str("</a>")?,
|
||||||
|
Container::Image(src, ..) => {
|
||||||
|
self.text_only = false;
|
||||||
|
if src.is_empty() {
|
||||||
|
self.out.write_str(r#"">"#)?;
|
||||||
|
} else {
|
||||||
|
write!(self.out, r#"" src="{}">"#, src)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Container::Verbatim => self.out.write_str("</code>")?,
|
Container::Verbatim => self.out.write_str("</code>")?,
|
||||||
Container::Math { .. } => self.out.write_str("</span>")?,
|
|
||||||
Container::RawBlock { .. } | Container::RawInline { .. } => {
|
Container::RawBlock { .. } | Container::RawInline { .. } => {
|
||||||
self.raw = Raw::None
|
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>")?,
|
||||||
|
|
|
@ -40,9 +40,12 @@ pub enum Container {
|
||||||
DisplayMath,
|
DisplayMath,
|
||||||
/// Span is the reference link tag.
|
/// Span is the reference link tag.
|
||||||
ReferenceLink,
|
ReferenceLink,
|
||||||
|
/// Span is the reference link tag.
|
||||||
/// Delimiter spans are the URL.
|
ReferenceImage,
|
||||||
|
/// Span is the URL.
|
||||||
InlineLink,
|
InlineLink,
|
||||||
|
/// Span is the URL.
|
||||||
|
InlineImage,
|
||||||
|
|
||||||
AutoLink,
|
AutoLink,
|
||||||
}
|
}
|
||||||
|
@ -70,7 +73,7 @@ pub struct Parser<I> {
|
||||||
/// Stack with kind and index of _potential_ openers for typesetting containers.
|
/// Stack with kind and index of _potential_ openers for typesetting containers.
|
||||||
typesets: Vec<(Container, usize)>,
|
typesets: Vec<(Container, usize)>,
|
||||||
/// Stack with index of _potential_ span/link openers.
|
/// Stack with index of _potential_ span/link openers.
|
||||||
spans: Vec<usize>,
|
spans: Vec<(usize, bool)>,
|
||||||
//attributes: Vec<(Span, usize)>,
|
//attributes: Vec<(Span, 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
|
||||||
/// characters are needed.
|
/// characters are needed.
|
||||||
|
@ -211,13 +214,14 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
|
|
||||||
fn parse_span(&mut self, first: &lex::Token) -> Option<Event> {
|
fn parse_span(&mut self, first: &lex::Token) -> Option<Event> {
|
||||||
match first.kind {
|
match first.kind {
|
||||||
lex::Kind::Open(Delimiter::Bracket) => Some(true),
|
lex::Kind::Sym(Symbol::ExclaimBracket) => Some((true, true)),
|
||||||
lex::Kind::Close(Delimiter::Bracket) => Some(false),
|
lex::Kind::Open(Delimiter::Bracket) => Some((true, false)),
|
||||||
|
lex::Kind::Close(Delimiter::Bracket) => Some((false, false)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
.and_then(|open| {
|
.and_then(|(open, img)| {
|
||||||
if open {
|
if open {
|
||||||
self.spans.push(self.events.len());
|
self.spans.push((self.events.len(), img));
|
||||||
// use str for now, replace if closed later
|
// use str for now, replace if closed later
|
||||||
Some(Event {
|
Some(Event {
|
||||||
kind: EventKind::Str,
|
kind: EventKind::Str,
|
||||||
|
@ -225,10 +229,13 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
})
|
})
|
||||||
} else if !self.spans.is_empty() {
|
} else if !self.spans.is_empty() {
|
||||||
let mut ahead = self.lexer.inner().clone();
|
let mut ahead = self.lexer.inner().clone();
|
||||||
|
let img = self.spans.last().unwrap().1;
|
||||||
match ahead.next() {
|
match ahead.next() {
|
||||||
Some(opener @ ('[' | '(')) => {
|
Some(opener @ ('[' | '(')) => {
|
||||||
let (closer, kind) = match opener {
|
let (closer, kind) = match opener {
|
||||||
|
'[' if img => (']', ReferenceImage),
|
||||||
'[' => (']', ReferenceLink),
|
'[' => (']', ReferenceLink),
|
||||||
|
'(' if img => (')', InlineImage),
|
||||||
'(' => (')', InlineLink),
|
'(' => (')', InlineLink),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
@ -251,7 +258,7 @@ impl<I: Iterator<Item = char> + Clone> Parser<I> {
|
||||||
}
|
}
|
||||||
.map(|(kind, span)| {
|
.map(|(kind, span)| {
|
||||||
self.lexer = lex::Lexer::new(ahead);
|
self.lexer = lex::Lexer::new(ahead);
|
||||||
let opener_event = self.spans.pop().unwrap();
|
let (opener_event, _) = self.spans.pop().unwrap();
|
||||||
self.events[opener_event].kind = EventKind::Enter(kind);
|
self.events[opener_event].kind = EventKind::Enter(kind);
|
||||||
self.events[opener_event].span = span;
|
self.events[opener_event].span = span;
|
||||||
self.span = span.translate(1);
|
self.span = span.translate(1);
|
||||||
|
@ -527,6 +534,12 @@ mod test {
|
||||||
(Str, "text"),
|
(Str, "text"),
|
||||||
(Exit(ReferenceLink), "tag"),
|
(Exit(ReferenceLink), "tag"),
|
||||||
);
|
);
|
||||||
|
test_parse!(
|
||||||
|
"![text][tag]",
|
||||||
|
(Enter(ReferenceImage), "tag"),
|
||||||
|
(Str, "text"),
|
||||||
|
(Exit(ReferenceImage), "tag"),
|
||||||
|
);
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"before [text][tag] after",
|
"before [text][tag] after",
|
||||||
(Str, "before "),
|
(Str, "before "),
|
||||||
|
|
11
src/lex.rs
11
src/lex.rs
|
@ -46,7 +46,7 @@ pub enum Symbol {
|
||||||
Asterisk,
|
Asterisk,
|
||||||
Caret,
|
Caret,
|
||||||
Equal,
|
Equal,
|
||||||
Exclaim,
|
ExclaimBracket,
|
||||||
Gt,
|
Gt,
|
||||||
Lt,
|
Lt,
|
||||||
Percentage,
|
Percentage,
|
||||||
|
@ -217,7 +217,10 @@ impl<I: Iterator<Item = char> + Clone> Lexer<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
'!' => Sym(Exclaim),
|
'!' if self.peek_char() == '[' => {
|
||||||
|
self.eat_char();
|
||||||
|
Sym(ExclaimBracket)
|
||||||
|
}
|
||||||
'%' => Sym(Percentage),
|
'%' => Sym(Percentage),
|
||||||
'<' => Sym(Lt),
|
'<' => Sym(Lt),
|
||||||
'>' => Sym(Gt),
|
'>' => Sym(Gt),
|
||||||
|
@ -349,12 +352,12 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn sym() {
|
fn sym() {
|
||||||
test_lex!(
|
test_lex!(
|
||||||
r#"'*^=!><%|+"~_"#,
|
r#"'*^=![><%|+"~_"#,
|
||||||
Sym(Quote1).l(1),
|
Sym(Quote1).l(1),
|
||||||
Sym(Asterisk).l(1),
|
Sym(Asterisk).l(1),
|
||||||
Sym(Caret).l(1),
|
Sym(Caret).l(1),
|
||||||
Sym(Equal).l(1),
|
Sym(Equal).l(1),
|
||||||
Sym(Exclaim).l(1),
|
Sym(ExclaimBracket).l(2),
|
||||||
Sym(Gt).l(1),
|
Sym(Gt).l(1),
|
||||||
Sym(Lt).l(1),
|
Sym(Lt).l(1),
|
||||||
Sym(Percentage).l(1),
|
Sym(Percentage).l(1),
|
||||||
|
|
41
src/lib.rs
41
src/lib.rs
|
@ -60,8 +60,8 @@ pub enum Container<'s> {
|
||||||
Span,
|
Span,
|
||||||
/// An inline link with a destination URL.
|
/// An inline link with a destination URL.
|
||||||
Link(CowStr<'s>, LinkType),
|
Link(CowStr<'s>, LinkType),
|
||||||
/// An inline image.
|
/// An inline image with a source URL.
|
||||||
Image(CowStr<'s>),
|
Image(CowStr<'s>, SpanLinkType),
|
||||||
/// An inline verbatim string.
|
/// An inline verbatim string.
|
||||||
Verbatim,
|
Verbatim,
|
||||||
/// An inline or display math element.
|
/// An inline or display math element.
|
||||||
|
@ -163,9 +163,14 @@ impl<'s> Container<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum LinkType {
|
pub enum SpanLinkType {
|
||||||
Inline,
|
Inline,
|
||||||
Reference,
|
Reference,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum LinkType {
|
||||||
|
Span(SpanLinkType),
|
||||||
AutoLink,
|
AutoLink,
|
||||||
Email,
|
Email,
|
||||||
}
|
}
|
||||||
|
@ -321,7 +326,6 @@ impl<'s> Parser<'s> {
|
||||||
|
|
||||||
impl<'s> Parser<'s> {
|
impl<'s> Parser<'s> {
|
||||||
fn inline(&self, inline: inline::Event) -> Event<'s> {
|
fn inline(&self, inline: inline::Event) -> Event<'s> {
|
||||||
//let content = inline.span.of(self.src);
|
|
||||||
match inline.kind {
|
match inline.kind {
|
||||||
inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
|
inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
|
||||||
let t = match c {
|
let t = match c {
|
||||||
|
@ -343,9 +347,13 @@ impl<'s> Parser<'s> {
|
||||||
inline::Container::DoubleQuoted => Container::DoubleQuoted,
|
inline::Container::DoubleQuoted => Container::DoubleQuoted,
|
||||||
inline::Container::InlineLink => Container::Link(
|
inline::Container::InlineLink => Container::Link(
|
||||||
self.inline_str(inline.span).replace('\n', "").into(),
|
self.inline_str(inline.span).replace('\n', "").into(),
|
||||||
LinkType::Inline,
|
LinkType::Span(SpanLinkType::Inline),
|
||||||
),
|
),
|
||||||
_ => todo!(),
|
inline::Container::InlineImage => Container::Image(
|
||||||
|
self.inline_str(inline.span).replace('\n', "").into(),
|
||||||
|
SpanLinkType::Inline,
|
||||||
|
),
|
||||||
|
_ => todo!("{:?}", c),
|
||||||
};
|
};
|
||||||
if matches!(inline.kind, inline::EventKind::Enter(_)) {
|
if matches!(inline.kind, inline::EventKind::Enter(_)) {
|
||||||
Event::Start(t, Attributes::none())
|
Event::Start(t, Attributes::none())
|
||||||
|
@ -470,6 +478,7 @@ mod test {
|
||||||
use super::CowStr;
|
use super::CowStr;
|
||||||
use super::Event::*;
|
use super::Event::*;
|
||||||
use super::LinkType;
|
use super::LinkType;
|
||||||
|
use super::SpanLinkType;
|
||||||
|
|
||||||
macro_rules! test_parse {
|
macro_rules! test_parse {
|
||||||
($src:expr $(,$($token:expr),* $(,)?)?) => {
|
($src:expr $(,$($token:expr),* $(,)?)?) => {
|
||||||
|
@ -616,11 +625,17 @@ mod test {
|
||||||
"[text](url)",
|
"[text](url)",
|
||||||
Start(Paragraph, Attributes::none()),
|
Start(Paragraph, Attributes::none()),
|
||||||
Start(
|
Start(
|
||||||
Link(CowStr::Borrowed("url"), LinkType::Inline),
|
Link(
|
||||||
|
CowStr::Borrowed("url"),
|
||||||
|
LinkType::Span(SpanLinkType::Inline),
|
||||||
|
),
|
||||||
Attributes::none()
|
Attributes::none()
|
||||||
),
|
),
|
||||||
Str(CowStr::Borrowed("text")),
|
Str(CowStr::Borrowed("text")),
|
||||||
End(Link(CowStr::Borrowed("url"), LinkType::Inline)),
|
End(Link(
|
||||||
|
CowStr::Borrowed("url"),
|
||||||
|
LinkType::Span(SpanLinkType::Inline)
|
||||||
|
)),
|
||||||
End(Paragraph),
|
End(Paragraph),
|
||||||
);
|
);
|
||||||
test_parse!(
|
test_parse!(
|
||||||
|
@ -631,11 +646,17 @@ mod test {
|
||||||
Start(Blockquote, Attributes::none()),
|
Start(Blockquote, Attributes::none()),
|
||||||
Start(Paragraph, Attributes::none()),
|
Start(Paragraph, Attributes::none()),
|
||||||
Start(
|
Start(
|
||||||
Link(CowStr::Owned("urlurl".to_string()), LinkType::Inline),
|
Link(
|
||||||
|
CowStr::Owned("urlurl".to_string()),
|
||||||
|
LinkType::Span(SpanLinkType::Inline)
|
||||||
|
),
|
||||||
Attributes::none()
|
Attributes::none()
|
||||||
),
|
),
|
||||||
Str(CowStr::Borrowed("text")),
|
Str(CowStr::Borrowed("text")),
|
||||||
End(Link(CowStr::Borrowed("urlurl"), LinkType::Inline)),
|
End(Link(
|
||||||
|
CowStr::Borrowed("urlurl"),
|
||||||
|
LinkType::Span(SpanLinkType::Inline)
|
||||||
|
)),
|
||||||
End(Paragraph),
|
End(Paragraph),
|
||||||
End(Blockquote),
|
End(Blockquote),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue