PR #27 Expose tag for unresolved links

Merge branch 'unresolved_links'

closes #27
This commit is contained in:
Noah Hellman 2023-03-20 23:46:22 +01:00
commit 5e2d567a54
4 changed files with 51 additions and 17 deletions

View file

@ -25,9 +25,11 @@
use crate::Alignment; use crate::Alignment;
use crate::Container; use crate::Container;
use crate::Event; use crate::Event;
use crate::LinkType;
use crate::ListKind; use crate::ListKind;
use crate::OrderedListNumbering::*; use crate::OrderedListNumbering::*;
use crate::Render; use crate::Render;
use crate::SpanLinkType;
pub struct Renderer; pub struct Renderer;
@ -161,11 +163,14 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
Container::DescriptionTerm => self.out.write_str("<dt")?, Container::DescriptionTerm => self.out.write_str("<dt")?,
Container::CodeBlock { .. } => self.out.write_str("<pre")?, Container::CodeBlock { .. } => self.out.write_str("<pre")?,
Container::Span | Container::Math { .. } => self.out.write_str("<span")?, Container::Span | Container::Math { .. } => self.out.write_str("<span")?,
Container::Link(dst, ..) => { Container::Link(dst, ty) => {
if dst.is_empty() { if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
self.out.write_str("<a")?; self.out.write_str("<a")?;
} else { } else {
self.out.write_str(r#"<a href=""#)?; self.out.write_str(r#"<a href=""#)?;
if matches!(ty, LinkType::Email) {
self.out.write_str("mailto:")?;
}
self.write_attr(dst)?; self.write_attr(dst)?;
self.out.write_char('"')?; self.out.write_char('"')?;
} }

View file

@ -213,9 +213,10 @@ pub enum Container<'s> {
CodeBlock { lang: Option<&'s str> }, CodeBlock { lang: Option<&'s str> },
/// An inline divider element. /// An inline divider element.
Span, 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), 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), Image(CowStr<'s>, SpanLinkType),
/// An inline verbatim string. /// An inline verbatim string.
Verbatim, Verbatim,
@ -333,6 +334,8 @@ pub enum SpanLinkType {
Inline, Inline,
/// In the form `[text][tag]` or `[tag][]`. /// In the form `[text][tag]` or `[tag][]`.
Reference, Reference,
/// Like reference, but the tag is unresolved.
Unresolved,
} }
/// The type of an inline link. /// The type of an inline link.
@ -729,29 +732,30 @@ impl<'s> Parser<'s> {
let link_def = let link_def =
self.pre_pass.link_definitions.get(tag.as_ref()).cloned(); 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); attributes.union(attrs_def);
url (url, SpanLinkType::Reference)
} else { } else {
self.pre_pass self.pre_pass.heading_id_by_tag(tag.as_ref()).map_or_else(
.heading_id_by_tag(tag.as_ref()) || (tag, SpanLinkType::Unresolved),
.map_or_else(|| "".into(), |id| format!("#{}", id).into()) |id| (format!("#{}", id).into(), SpanLinkType::Reference),
)
}; };
if matches!(c, inline::Container::ReferenceLink) { if matches!(c, inline::Container::ReferenceLink) {
Container::Link(url, LinkType::Span(SpanLinkType::Reference)) Container::Link(url_or_tag, LinkType::Span(ty))
} else { } else {
Container::Image(url, SpanLinkType::Reference) Container::Image(url_or_tag, ty)
} }
} }
inline::Container::Autolink => { inline::Container::Autolink => {
let url = self.inlines.src(inline.span); let url = self.inlines.src(inline.span);
let url = if url.contains('@') { let ty = if url.contains('@') {
format!("mailto:{}", url).into() LinkType::Email
} else { } else {
url LinkType::AutoLink
}; };
Container::Link(url, LinkType::AutoLink) Container::Link(url, ty)
} }
}; };
if matches!(inline.kind, inline::EventKind::Enter(_)) { 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] #[test]
fn link_reference_multiline() { fn link_reference_multiline() {
test_parse!( test_parse!(

View file

@ -1,3 +1,3 @@
block_list_flat:large list marker number 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 inline_links_nested:empty link text

View file

@ -10,7 +10,6 @@ e1f5b5e:untrimmed whitespace before linebreak
07888f3:div close within raw block 07888f3:div close within raw block
8423412:heading id conflict with existing id 8423412:heading id conflict with existing id
00a46ed:clear inline formatting from link tags 00a46ed:clear inline formatting from link tags
a8e17c3:empty href
c0a3dec:escape in url c0a3dec:escape in url
e66af00:url container precedence e66af00:url container precedence
61876cf:roman alpha ambiguity 61876cf:roman alpha ambiguity