sable-markdown/src/render/inline.rs@main
raw
1use pretty::{Arena, DocAllocator, DocBuilder};
2
3use crate::{
4 ast::{Image, Inline, Link, LinkDefinition, Tag, Wikilink},
5 render::{
6 Config, Context, ToDoc,
7 util::{ArenaExt as _, escape},
8 },
9};
10
11impl<'a> ToDoc<'a> for Vec<Inline> {
12 fn to_doc(
13 &self,
14 config: &'a Config<'a>,
15 context: &'a Context,
16 arena: &'a Arena<'a>,
17 ) -> DocBuilder<'a, Arena<'a>, ()> {
18 arena.concat(
19 self.iter()
20 .map(|inline| inline.to_doc(config, context, arena))
21 .collect::<Vec<_>>(),
22 )
23 }
24}
25
26impl<'a> ToDoc<'a> for Inline {
27 fn to_doc(
28 &self,
29 config: &'a Config<'a>,
30 context: &'a Context,
31 arena: &'a Arena<'a>,
32 ) -> DocBuilder<'a, Arena<'a>, ()> {
33 match self {
34 Self::Text(t) => arena.text(escape(t)),
35 Self::LineBreak => arena.tag("br", vec![], arena.nil()),
36 Self::Code(code) => arena.tag("code", vec![], arena.text(escape(code))),
37 Self::Html(html) => arena.text(html.clone()),
38 Self::Emphasis(children) => {
39 arena.tag("em", vec![], children.to_doc(config, context, arena))
40 }
41 Self::Strong(children) => {
42 arena.tag("b", vec![], children.to_doc(config, context, arena))
43 }
44 Self::Strikethrough(children) => {
45 arena.tag("s", vec![], children.to_doc(config, context, arena))
46 }
47 Self::Tag(Tag { text }) => arena.tag(
48 "a",
49 vec![("data-tag".to_owned(), String::new())],
50 arena.text(text.to_owned()),
51 ),
52 Self::Wikilink(Wikilink { link, target, name }) => {
53 let destination = config.rewrite_wikilink(link, target.as_deref());
54
55 arena.tag(
56 "a",
57 vec![("href".to_owned(), escape(&destination))],
58 arena.text(name.as_ref().unwrap_or(link).to_owned()),
59 )
60 }
61 Self::Link(Link {
62 destination,
63 title,
64 children,
65 }) => {
66 let destination = config.rewrite_link(destination, None);
67 let mut attributes = vec![("href".to_owned(), escape(&destination))];
68 if let Some(title) = title {
69 attributes.push(("title".to_owned(), escape(title)));
70 }
71 arena.tag("a", attributes, children.to_doc(config, context, arena))
72 }
73 Self::Image(Image {
74 destination,
75 title,
76 alt,
77 }) => {
78 let mut attributes = vec![
79 ("src".to_owned(), escape(destination)),
80 ("alt".to_owned(), escape(alt)),
81 ];
82 if let Some(title) = title {
83 attributes.push(("title".to_owned(), escape(title)));
84 }
85 arena.tag("img", attributes, arena.nil())
86 }
87 Self::Autolink(link) => {
88 let destination = config.rewrite_link(link, None);
89
90 arena.tag(
91 "a",
92 vec![("href".to_owned(), escape(&destination))],
93 arena.text(escape(link)),
94 )
95 }
96 Self::FootnoteReference(label) => {
97 let Some(index) = context.get_footnote_index(label) else {
98 return arena.nil();
99 };
100 arena.tag(
101 "a",
102 vec![
103 ("class".to_owned(), "footnote-reference".to_owned()),
104 ("href".to_owned(), escape(&format!("#footnote-{index}"))),
105 ],
106 arena.text(format!("[{index}]")),
107 )
108 }
109 Self::LinkReference(v) => {
110 let definition: &LinkDefinition = match context.get_link_definition(&v.label) {
111 Some(v) => v,
112 None => return arena.nil(),
113 };
114 let destination = config.rewrite_link(&definition.destination, None);
115 let mut attributes = vec![("href".to_owned(), escape(&destination))];
116 if let Some(title) = &definition.title {
117 attributes.push(("title".to_owned(), escape(title)));
118 }
119 arena.tag("a", attributes, v.text.to_doc(config, context, arena))
120 }
121 Self::Empty => arena.nil(),
122 }
123 }
124}
125