Skip to main content

typst_eval/
markup.rs

1use typst_library::diag::{At, SourceResult, warning};
2use typst_library::foundations::{
3    Content, Label, NativeElement, Repr, Smart, Symbol, Unlabellable, Value,
4};
5use typst_library::model::{
6    EmphElem, EnumItem, HeadingElem, LinkElem, ListItem, ParbreakElem, RefElem,
7    StrongElem, Supplement, TermItem, Url,
8};
9use typst_library::text::{
10    LinebreakElem, RawContent, RawElem, SmartQuoteElem, SpaceElem, TextElem,
11};
12use typst_syntax::ast::{self, AstNode};
13use typst_utils::PicoStr;
14
15use crate::{Eval, Vm};
16
17impl Eval for ast::Markup<'_> {
18    type Output = Content;
19
20    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
21        eval_markup(vm, &mut self.exprs())
22    }
23}
24
25/// Evaluate a stream of markup.
26fn eval_markup<'a>(
27    vm: &mut Vm,
28    exprs: &mut impl Iterator<Item = ast::Expr<'a>>,
29) -> SourceResult<Content> {
30    let flow = vm.flow.take();
31    let mut seq = Vec::with_capacity(exprs.size_hint().1.unwrap_or_default());
32
33    while let Some(expr) = exprs.next() {
34        match expr {
35            ast::Expr::SetRule(set) => {
36                let styles = set.eval(vm)?;
37                if vm.flow.is_some() {
38                    break;
39                }
40
41                seq.push(eval_markup(vm, exprs)?.styled_with_map(styles))
42            }
43            ast::Expr::ShowRule(show) => {
44                let recipe = show.eval(vm)?;
45                if vm.flow.is_some() {
46                    break;
47                }
48
49                let tail = eval_markup(vm, exprs)?;
50                seq.push(tail.styled_with_recipe(&mut vm.engine, vm.context, recipe)?)
51            }
52            expr => match expr.eval(vm)? {
53                Value::Label(label) => {
54                    if let Some(elem) =
55                        seq.iter_mut().rev().find(|node| !node.can::<dyn Unlabellable>())
56                    {
57                        if elem.label().is_some() {
58                            vm.engine.sink.warn(warning!(
59                                elem.span(), "content labelled multiple times";
60                                hint: "only the last label is used, the rest are ignored";
61                            ));
62                        }
63
64                        *elem = std::mem::take(elem).labelled(label);
65                    } else {
66                        vm.engine.sink.warn(warning!(
67                            expr.span(),
68                            "label `{}` is not attached to anything",
69                            label.repr(),
70                        ));
71                    }
72                }
73                value => seq.push(value.display().spanned(expr.span())),
74            },
75        }
76
77        if vm.flow.is_some() {
78            break;
79        }
80    }
81
82    if flow.is_some() {
83        vm.flow = flow;
84    }
85
86    Ok(Content::sequence(seq))
87}
88
89impl Eval for ast::Text<'_> {
90    type Output = Content;
91
92    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
93        Ok(TextElem::packed(self.get().clone()))
94    }
95}
96
97impl Eval for ast::Space<'_> {
98    type Output = Content;
99
100    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
101        Ok(SpaceElem::shared().clone())
102    }
103}
104
105impl Eval for ast::Linebreak<'_> {
106    type Output = Content;
107
108    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
109        Ok(LinebreakElem::shared().clone())
110    }
111}
112
113impl Eval for ast::Parbreak<'_> {
114    type Output = Content;
115
116    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
117        Ok(ParbreakElem::shared().clone())
118    }
119}
120
121impl Eval for ast::Escape<'_> {
122    type Output = Value;
123
124    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
125        Ok(Value::Symbol(Symbol::runtime_char(self.get())))
126    }
127}
128
129impl Eval for ast::Shorthand<'_> {
130    type Output = Value;
131
132    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
133        Ok(Value::Symbol(Symbol::runtime_char(self.get())))
134    }
135}
136
137impl Eval for ast::SmartQuote<'_> {
138    type Output = Content;
139
140    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
141        Ok(SmartQuoteElem::new().with_double(self.double()).pack())
142    }
143}
144
145impl Eval for ast::Strong<'_> {
146    type Output = Content;
147
148    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
149        let body = self.body().eval(vm)?;
150        Ok(StrongElem::new(body).pack())
151    }
152}
153
154impl Eval for ast::Emph<'_> {
155    type Output = Content;
156
157    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
158        let body = self.body().eval(vm)?;
159        Ok(EmphElem::new(body).pack())
160    }
161}
162
163impl Eval for ast::Raw<'_> {
164    type Output = Content;
165
166    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
167        let lines = self.lines().map(|line| (line.get().clone(), line.span())).collect();
168        let mut elem = RawElem::new(RawContent::Lines(lines)).with_block(self.block());
169        if let Some(lang) = self.lang() {
170            elem.lang.set(Some(lang.get().clone()));
171        }
172        Ok(elem.pack())
173    }
174}
175
176impl Eval for ast::Link<'_> {
177    type Output = Content;
178
179    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
180        let url = Url::new(self.get().clone()).at(self.span())?;
181        Ok(LinkElem::from_url(url).pack())
182    }
183}
184
185impl Eval for ast::Label<'_> {
186    type Output = Value;
187
188    fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
189        Ok(Value::Label(
190            Label::new(PicoStr::intern(self.get())).expect("unexpected empty label"),
191        ))
192    }
193}
194
195impl Eval for ast::Ref<'_> {
196    type Output = Content;
197
198    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
199        let target = Label::new(PicoStr::intern(self.target()))
200            .expect("unexpected empty reference");
201        let mut elem = RefElem::new(target);
202        if let Some(supplement) = self.supplement() {
203            elem.supplement
204                .set(Smart::Custom(Some(Supplement::Content(supplement.eval(vm)?))));
205        }
206        Ok(elem.pack())
207    }
208}
209
210impl Eval for ast::Heading<'_> {
211    type Output = Content;
212
213    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
214        let depth = self.depth();
215        let body = self.body().eval(vm)?;
216        Ok(HeadingElem::new(body).with_depth(depth).pack())
217    }
218}
219
220impl Eval for ast::ListItem<'_> {
221    type Output = Content;
222
223    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
224        Ok(ListItem::new(self.body().eval(vm)?).pack())
225    }
226}
227
228impl Eval for ast::EnumItem<'_> {
229    type Output = Content;
230
231    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
232        let body = self.body().eval(vm)?;
233        let mut elem = EnumItem::new(body);
234        if let Some(number) = self.number() {
235            elem.number.set(Smart::Custom(number));
236        }
237        Ok(elem.pack())
238    }
239}
240
241impl Eval for ast::TermItem<'_> {
242    type Output = Content;
243
244    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
245        let term = self.term().eval(vm)?;
246        let description = self.description().eval(vm)?;
247        Ok(TermItem::new(term, description).pack())
248    }
249}