wayver's git archive


an obsidian renderer
git clone https://git.wayver.dev/sable

sable-markdown/src/render/inline.rs@2b84405277e54ab809e328cf0237374d4b4dbd0c

raw
Date Commit Message Author Files + -
2026-02-23 01:55 initial mvp wayverd 139 17808 0
...

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