typst_eval/
rules.rs

1use typst_library::diag::{At, SourceResult, warning};
2use typst_library::foundations::{
3    Element, Func, Recipe, Selector, ShowableSelector, Styles, Transformation,
4};
5use typst_library::layout::BlockElem;
6use typst_library::model::ParElem;
7use typst_syntax::ast::{self, AstNode};
8
9use crate::{Eval, Vm};
10
11impl Eval for ast::SetRule<'_> {
12    type Output = Styles;
13
14    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
15        if let Some(condition) = self.condition()
16            && !condition.eval(vm)?.cast::<bool>().at(condition.span())?
17        {
18            return Ok(Styles::new());
19        }
20
21        let target = self.target();
22        let target = target
23            .eval(vm)?
24            .cast::<Func>()
25            .and_then(|func| {
26                func.element().ok_or_else(|| {
27                    "only element functions can be used in set rules".into()
28                })
29            })
30            .at(target.span())?;
31        let args = self.args().eval(vm)?.spanned(self.span());
32        Ok(target.set(&mut vm.engine, args)?.spanned(self.span()).liftable())
33    }
34}
35
36impl Eval for ast::ShowRule<'_> {
37    type Output = Recipe;
38
39    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
40        let selector = self
41            .selector()
42            .map(|sel| sel.eval(vm)?.cast::<ShowableSelector>().at(sel.span()))
43            .transpose()?
44            .map(|selector| selector.0);
45
46        let transform = self.transform();
47        let transform = match transform {
48            ast::Expr::SetRule(set) => Transformation::Style(set.eval(vm)?),
49            expr => expr.eval(vm)?.cast::<Transformation>().at(transform.span())?,
50        };
51
52        let recipe = Recipe::new(selector, transform, self.span());
53        check_show_par_set_block(vm, &recipe);
54
55        Ok(recipe)
56    }
57}
58
59/// Migration hint for `show par: set block(spacing: ..)`.
60fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
61    if let Some(Selector::Elem(elem, _)) = recipe.selector()
62        && *elem == Element::of::<ParElem>()
63        && let Transformation::Style(styles) = recipe.transform()
64        && (styles.has(BlockElem::above) || styles.has(BlockElem::below))
65    {
66        vm.engine.sink.warn(warning!(
67                recipe.span(),
68                "`show par: set block(spacing: ..)` has no effect anymore";
69                hint: "write `set par(spacing: ..)` instead";
70                hint: "this is specific to paragraphs as they are not considered blocks anymore"
71            ))
72    }
73}