summaryrefslogtreecommitdiff
path: root/crates/typst-eval/src/rules.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-eval/src/rules.rs')
-rw-r--r--crates/typst-eval/src/rules.rs76
1 files changed, 76 insertions, 0 deletions
diff --git a/crates/typst-eval/src/rules.rs b/crates/typst-eval/src/rules.rs
new file mode 100644
index 00000000..646354d4
--- /dev/null
+++ b/crates/typst-eval/src/rules.rs
@@ -0,0 +1,76 @@
+use typst_library::diag::{warning, At, SourceResult};
+use typst_library::foundations::{
+ Element, Fields, Func, Recipe, Selector, ShowableSelector, Styles, Transformation,
+};
+use typst_library::layout::BlockElem;
+use typst_library::model::ParElem;
+use typst_syntax::ast::{self, AstNode};
+
+use crate::{Eval, Vm};
+
+impl Eval for ast::SetRule<'_> {
+ type Output = Styles;
+
+ fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ if let Some(condition) = self.condition() {
+ if !condition.eval(vm)?.cast::<bool>().at(condition.span())? {
+ return Ok(Styles::new());
+ }
+ }
+
+ let target = self.target();
+ let target = target
+ .eval(vm)?
+ .cast::<Func>()
+ .and_then(|func| {
+ func.element().ok_or_else(|| {
+ "only element functions can be used in set rules".into()
+ })
+ })
+ .at(target.span())?;
+ let args = self.args().eval(vm)?.spanned(self.span());
+ Ok(target.set(&mut vm.engine, args)?.spanned(self.span()).liftable())
+ }
+}
+
+impl Eval for ast::ShowRule<'_> {
+ type Output = Recipe;
+
+ fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ let selector = self
+ .selector()
+ .map(|sel| sel.eval(vm)?.cast::<ShowableSelector>().at(sel.span()))
+ .transpose()?
+ .map(|selector| selector.0);
+
+ let transform = self.transform();
+ let transform = match transform {
+ ast::Expr::Set(set) => Transformation::Style(set.eval(vm)?),
+ expr => expr.eval(vm)?.cast::<Transformation>().at(transform.span())?,
+ };
+
+ let recipe = Recipe::new(selector, transform, self.span());
+ check_show_par_set_block(vm, &recipe);
+
+ Ok(recipe)
+ }
+}
+
+/// Migration hint for `show par: set block(spacing: ..)`.
+fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
+ if_chain::if_chain! {
+ if let Some(Selector::Elem(elem, _)) = recipe.selector();
+ if *elem == Element::of::<ParElem>();
+ if let Transformation::Style(styles) = recipe.transform();
+ if styles.has::<BlockElem>(<BlockElem as Fields>::Enum::Above as _) ||
+ styles.has::<BlockElem>(<BlockElem as Fields>::Enum::Below as _);
+ then {
+ vm.engine.sink.warn(warning!(
+ recipe.span(),
+ "`show par: set block(spacing: ..)` has no effect anymore";
+ hint: "write `set par(spacing: ..)` instead";
+ hint: "this is specific to paragraphs as they are not considered blocks anymore"
+ ))
+ }
+ }
+}