diff --git a/src/html.rs b/src/html.rs index 1a1d9e0..1b825f8 100644 --- a/src/html.rs +++ b/src/html.rs @@ -25,9 +25,11 @@ use crate::Alignment; use crate::Container; use crate::Event; +use crate::LinkType; use crate::ListKind; use crate::OrderedListNumbering::*; use crate::Render; +use crate::SpanLinkType; pub struct Renderer; @@ -161,11 +163,14 @@ impl<'s, I: Iterator>, W: std::fmt::Write> Writer<'s, I, W> { Container::DescriptionTerm => self.out.write_str(" self.out.write_str(" self.out.write_str(" { - if dst.is_empty() { + Container::Link(dst, ty) => { + if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) { self.out.write_str(" { CodeBlock { lang: Option<&'s str> }, /// An inline divider element. Span, - /// An inline link with a destination URL. + /// An inline link, the first field is either a destination URL or an unresolved tag. Link(CowStr<'s>, LinkType), - /// An inline image with a source URL. Inner Str objects compose the alternative text. + /// An inline image, the first field is either a destination URL or an unresolved tag. Inner + /// Str objects compose the alternative text. Image(CowStr<'s>, SpanLinkType), /// An inline verbatim string. Verbatim, @@ -333,6 +334,8 @@ pub enum SpanLinkType { Inline, /// In the form `[text][tag]` or `[tag][]`. Reference, + /// Like reference, but the tag is unresolved. + Unresolved, } /// The type of an inline link. @@ -729,29 +732,30 @@ impl<'s> Parser<'s> { let link_def = self.pre_pass.link_definitions.get(tag.as_ref()).cloned(); - let url = if let Some((url, attrs_def)) = link_def { + let (url_or_tag, ty) = if let Some((url, attrs_def)) = link_def { attributes.union(attrs_def); - url + (url, SpanLinkType::Reference) } else { - self.pre_pass - .heading_id_by_tag(tag.as_ref()) - .map_or_else(|| "".into(), |id| format!("#{}", id).into()) + self.pre_pass.heading_id_by_tag(tag.as_ref()).map_or_else( + || (tag, SpanLinkType::Unresolved), + |id| (format!("#{}", id).into(), SpanLinkType::Reference), + ) }; if matches!(c, inline::Container::ReferenceLink) { - Container::Link(url, LinkType::Span(SpanLinkType::Reference)) + Container::Link(url_or_tag, LinkType::Span(ty)) } else { - Container::Image(url, SpanLinkType::Reference) + Container::Image(url_or_tag, ty) } } inline::Container::Autolink => { let url = self.inlines.src(inline.span); - let url = if url.contains('@') { - format!("mailto:{}", url).into() + let ty = if url.contains('@') { + LinkType::Email } else { - url + LinkType::AutoLink }; - Container::Link(url, LinkType::AutoLink) + Container::Link(url, ty) } }; if matches!(inline.kind, inline::EventKind::Enter(_)) { @@ -1298,6 +1302,32 @@ mod test { ); } + #[test] + fn link_reference_unresolved() { + test_parse!( + "[text][tag]", + Start(Paragraph, Attributes::new()), + Start( + Link("tag".into(), LinkType::Span(SpanLinkType::Unresolved)), + Attributes::new() + ), + Str("text".into()), + End(Link("tag".into(), LinkType::Span(SpanLinkType::Unresolved))), + End(Paragraph), + ); + test_parse!( + "![text][tag]", + Start(Paragraph, Attributes::new()), + Start( + Image("tag".into(), SpanLinkType::Unresolved), + Attributes::new() + ), + Str("text".into()), + End(Image("tag".into(), SpanLinkType::Unresolved)), + End(Paragraph), + ); + } + #[test] fn link_reference_multiline() { test_parse!( diff --git a/tests/bench/skip b/tests/bench/skip index d9afe9e..33a6308 100644 --- a/tests/bench/skip +++ b/tests/bench/skip @@ -1,3 +1,3 @@ block_list_flat:large list marker number -inline_links_flat:escaped attributes, empty hrefs +inline_links_flat:space before img, img attrs order inline_links_nested:empty link text diff --git a/tests/suite/skip b/tests/suite/skip index aea68c3..e9c249e 100644 --- a/tests/suite/skip +++ b/tests/suite/skip @@ -10,7 +10,6 @@ e1f5b5e:untrimmed whitespace before linebreak 07888f3:div close within raw block 8423412:heading id conflict with existing id 00a46ed:clear inline formatting from link tags -a8e17c3:empty href c0a3dec:escape in url e66af00:url container precedence 61876cf:roman alpha ambiguity