parser: apply link def attrs to link

This commit is contained in:
Noah Hellman 2023-01-28 16:03:01 +01:00
parent 9fdd402d07
commit 2e4a9147aa
2 changed files with 63 additions and 15 deletions

View file

@ -58,6 +58,21 @@ impl<'s> Attributes<'s> {
true true
} }
/// Combine all attributes from both objects, prioritizing self on conflicts.
pub fn union(&mut self, other: Self) {
if let Some(attrs0) = &mut self.0 {
if let Some(mut attrs1) = other.0 {
for (attr, val) in attrs1.drain(..) {
if !attrs0.iter().any(|(a, _)| *a == attr) {
attrs0.push((attr, val));
}
}
}
} else {
self.0 = other.0;
}
}
fn add(&mut self, attr: &'s str, val: CowStr<'s>) { fn add(&mut self, attr: &'s str, val: CowStr<'s>) {
if self.0.is_none() { if self.0.is_none() {
self.0 = Some(Vec::new().into()); self.0 = Some(Vec::new().into());

View file

@ -318,7 +318,7 @@ pub struct Parser<'s> {
src: &'s str, src: &'s str,
/// Link definitions encountered during block parse, written once. /// Link definitions encountered during block parse, written once.
link_definitions: std::collections::HashMap<&'s str, CowStr<'s>>, link_definitions: std::collections::HashMap<&'s str, (CowStr<'s>, attr::Attributes<'s>)>,
/// Block tree cursor. /// Block tree cursor.
tree: block::Tree, tree: block::Tree,
@ -347,17 +347,24 @@ impl<'s> Parser<'s> {
let link_definitions = { let link_definitions = {
let mut branch = tree.clone(); let mut branch = tree.clone();
let mut defs = std::collections::HashMap::new(); let mut defs = std::collections::HashMap::new();
let mut attr_prev: Option<Span> = None;
while let Some(e) = branch.next() { while let Some(e) = branch.next() {
if let tree::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition)) = if let tree::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition)) =
e.kind e.kind
{ {
let tag = e.span.of(src); let tag = e.span.of(src);
let attrs =
attr_prev.map_or_else(Attributes::new, |sp| attr::parse(sp.of(src)));
let url = match branch.count_children() { let url = match branch.count_children() {
0 => "".into(), 0 => "".into(),
1 => branch.take_inlines().next().unwrap().of(src).trim().into(), 1 => branch.take_inlines().next().unwrap().of(src).trim().into(),
_ => branch.take_inlines().map(|sp| sp.of(src).trim()).collect(), _ => branch.take_inlines().map(|sp| sp.of(src).trim()).collect(),
}; };
defs.insert(tag, url); defs.insert(tag, (url, attrs));
} else if let tree::EventKind::Atom(block::Atom::Attributes) = e.kind {
attr_prev = Some(e.span);
} else {
attr_prev = None;
} }
} }
defs defs
@ -386,7 +393,7 @@ impl<'s> Parser<'s> {
let mut inline = parser.next(); let mut inline = parser.next();
let mut first_is_attr = false; let mut first_is_attr = false;
let attributes = inline.as_ref().map_or_else(Attributes::new, |inl| { let mut attributes = inline.as_ref().map_or_else(Attributes::new, |inl| {
if let inline::EventKind::Attributes = inl.kind { if let inline::EventKind::Attributes = inl.kind {
first_is_attr = true; first_is_attr = true;
attr::parse(self.inlines.slice(inl.span)) attr::parse(self.inlines.slice(inl.span))
@ -433,20 +440,19 @@ impl<'s> Parser<'s> {
}, },
SpanLinkType::Inline, SpanLinkType::Inline,
), ),
inline::Container::ReferenceLink => Container::Link( inline::Container::ReferenceLink | inline::Container::ReferenceImage => {
self.link_definitions let (url, attrs_def) = self
.link_definitions
.get(self.inlines.src(inline.span).replace('\n', " ").as_str()) .get(self.inlines.src(inline.span).replace('\n', " ").as_str())
.cloned() .cloned()
.unwrap_or_else(|| "".into()), .unwrap_or_else(|| ("".into(), Attributes::new()));
LinkType::Span(SpanLinkType::Reference), attributes.union(attrs_def);
), if matches!(c, inline::Container::ReferenceLink) {
inline::Container::ReferenceImage => Container::Image( Container::Link(url, LinkType::Span(SpanLinkType::Reference))
self.link_definitions } else {
.get(self.inlines.src(inline.span).replace('\n', " ").as_str()) Container::Image(url, SpanLinkType::Reference)
.cloned() }
.unwrap_or_else(|| "".into()), }
SpanLinkType::Reference,
),
inline::Container::Autolink => todo!("{:?}", c), inline::Container::Autolink => todo!("{:?}", c),
}; };
if matches!(inline.kind, inline::EventKind::Enter(_)) { if matches!(inline.kind, inline::EventKind::Enter(_)) {
@ -921,6 +927,33 @@ mod test {
); );
} }
#[test]
fn link_reference_attrs() {
test_parse!(
concat!(
"[text][tag]{b=c}\n",
"\n",
"{a=b}\n",
"[tag]: url\n",
"\n",
"para\n",
),
Start(Paragraph, Attributes::new()),
Start(
Link("url".into(), LinkType::Span(SpanLinkType::Reference)),
[("b", "c"), ("a", "b")].into_iter().collect(),
),
Str("text".into()),
End(Link("url".into(), LinkType::Span(SpanLinkType::Reference))),
End(Paragraph),
Atom(Blankline),
Atom(Blankline),
Start(Paragraph, Attributes::new()),
Str("para".into()),
End(Paragraph),
);
}
#[test] #[test]
fn footnote_references() { fn footnote_references() {
test_parse!( test_parse!(