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