From efd1853d069fbd1476e82d015da4d0d04cfaccc0 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 7 Nov 2022 12:21:12 +0100 Subject: [PATCH] Show it! - New show rule syntax - Set if syntax - Removed wrap syntax --- library/src/layout/page.rs | 2 +- library/src/math/mod.rs | 17 ++- library/src/prelude.rs | 4 +- library/src/structure/heading.rs | 20 +-- library/src/structure/list.rs | 34 +++-- library/src/structure/reference.rs | 20 +-- library/src/structure/table.rs | 24 +-- library/src/text/deco.rs | 27 ++-- library/src/text/link.rs | 54 +++---- library/src/text/mod.rs | 44 +++--- library/src/text/raw.rs | 26 ++-- library/src/text/shift.rs | 16 +- macros/src/lib.rs | 14 +- src/model/cast.rs | 11 +- src/model/content.rs | 33 ++++- src/model/eval.rs | 69 +++------ src/model/func.rs | 16 +- src/model/items.rs | 16 +- src/model/ops.rs | 8 +- src/model/styles.rs | 107 ++++++++------ src/model/value.rs | 2 +- src/syntax/ast.rs | 135 +++++++---------- src/syntax/highlight.rs | 12 +- src/syntax/kind.rs | 70 ++++----- src/syntax/parsing.rs | 153 ++++++-------------- src/syntax/tokens.rs | 2 - tests/ref/style/set.png | Bin 20825 -> 24880 bytes tests/ref/style/{wrap.png => show-bare.png} | Bin 24846 -> 25853 bytes tests/ref/text/code.png | Bin 38900 -> 40777 bytes tests/src/tests.rs | 2 +- tests/typ/base/eval.typ | 11 +- tests/typ/code/break-continue.typ | 2 +- tests/typ/code/field.typ | 4 +- tests/typ/code/import.typ | 2 +- tests/typ/code/include.typ | 2 +- tests/typ/graphics/shape-rect.typ | 4 +- tests/typ/structure/desc.typ | 2 +- tests/typ/structure/heading.typ | 6 +- tests/typ/style/set.typ | 13 ++ tests/typ/style/{wrap.typ => show-bare.typ} | 23 ++- tests/typ/style/show-node.typ | 45 +++--- tests/typ/style/show-recursive.typ | 23 +-- tests/typ/style/show-text.typ | 32 ++-- tests/typ/text/code.typ | 1 + tools/support/typst.tmLanguage.json | 9 +- tools/test-helper/extension.js | 4 +- 46 files changed, 537 insertions(+), 584 deletions(-) rename tests/ref/style/{wrap.png => show-bare.png} (80%) rename tests/typ/style/{wrap.typ => show-bare.typ} (55%) diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index e1af6ec55..c308571ca 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -200,7 +200,7 @@ impl Cast> for Marginal { fn cast(value: Spanned) -> StrResult { match value.v { Value::None => Ok(Self::None), - Value::Str(v) => Ok(Self::Content(TextNode(v.into()).pack())), + Value::Str(v) => Ok(Self::Content(TextNode::packed(v))), Value::Content(v) => Ok(Self::Content(v)), Value::Func(v) => Ok(Self::Func(v, value.span)), v => Err(format!( diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index dae869ede..0fad2939f 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -18,7 +18,7 @@ pub struct MathNode { pub display: bool, } -#[node(Show, LayoutInline, Texify)] +#[node(Show, Finalize, LayoutInline, Texify)] impl MathNode { /// The math font family. #[property(referenced)] @@ -29,6 +29,13 @@ impl MathNode { /// The spacing below display math. #[property(resolve, shorthand(around))] pub const BELOW: Option = Some(Ratio::one().into()); + + fn field(&self, name: &str) -> Option { + match name { + "display" => Some(Value::Bool(self.display)), + _ => None, + } + } } impl Show for MathNode { @@ -36,18 +43,16 @@ impl Show for MathNode { self.clone().pack() } - fn field(&self, _: &str) -> Option { - None - } - - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { Ok(if self.display { self.clone().pack().aligned(Axes::with_x(Some(Align::Center.into()))) } else { self.clone().pack() }) } +} +impl Finalize for MathNode { fn finalize( &self, _: Tracked, diff --git a/library/src/prelude.rs b/library/src/prelude.rs index 11095c67c..f51b826fd 100644 --- a/library/src/prelude.rs +++ b/library/src/prelude.rs @@ -9,8 +9,8 @@ pub use typst::frame::*; pub use typst::geom::*; pub use typst::model::{ array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast, - Content, Dict, Fold, Func, Key, Node, Resolve, Scope, Selector, Show, Smart, Str, - StyleChain, StyleMap, StyleVec, Value, Vm, + Content, Dict, Finalize, Fold, Func, Key, Node, Resolve, Scope, Selector, Show, + Smart, Str, StyleChain, StyleMap, StyleVec, Value, Vm, }; pub use typst::syntax::{Span, Spanned}; pub use typst::util::{format_eco, EcoString}; diff --git a/library/src/structure/heading.rs b/library/src/structure/heading.rs index 62a670009..46e98c186 100644 --- a/library/src/structure/heading.rs +++ b/library/src/structure/heading.rs @@ -12,7 +12,7 @@ pub struct HeadingNode { pub body: Content, } -#[node(Show)] +#[node(Show, Finalize)] impl HeadingNode { /// The heading's font family. Just the normal text family if `auto`. #[property(referenced)] @@ -67,12 +67,6 @@ impl HeadingNode { } .pack()) } -} - -impl Show for HeadingNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { body: self.body.unguard(sel), ..*self }.pack() - } fn field(&self, name: &str) -> Option { match name { @@ -81,11 +75,19 @@ impl Show for HeadingNode { _ => None, } } +} - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { - Ok(BlockNode(self.body.clone()).pack()) +impl Show for HeadingNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self { body: self.body.unguard(sel), ..*self }.pack() } + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { + Ok(BlockNode(self.body.clone()).pack()) + } +} + +impl Finalize for HeadingNode { fn finalize( &self, world: Tracked, diff --git a/library/src/structure/list.rs b/library/src/structure/list.rs index a5e1380ad..499207a4c 100644 --- a/library/src/structure/list.rs +++ b/library/src/structure/list.rs @@ -22,7 +22,7 @@ pub type EnumNode = ListNode; /// A description list. pub type DescNode = ListNode; -#[node(Show)] +#[node(Show, Finalize)] impl ListNode { /// How the list is labelled. #[property(referenced)] @@ -80,16 +80,6 @@ impl ListNode { } .pack()) } -} - -impl Show for ListNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { - items: self.items.map(|item| item.unguard(sel)), - ..*self - } - .pack() - } fn field(&self, name: &str) -> Option { match name { @@ -101,8 +91,18 @@ impl Show for ListNode { _ => None, } } +} - fn realize( +impl Show for ListNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self { + items: self.items.map(|item| item.unguard(sel)), + ..*self + } + .pack() + } + + fn show( &self, world: Tracked, styles: StyleChain, @@ -140,7 +140,7 @@ impl Show for ListNode { ListItem::Enum(_, body) => body.as_ref().clone(), ListItem::Desc(item) => Content::sequence(vec![ HNode { amount: (-body_indent).into(), weak: false }.pack(), - (item.term.clone() + TextNode(':'.into()).pack()).strong(), + (item.term.clone() + TextNode::packed(':')).strong(), SpaceNode.pack(), item.body.clone(), ]), @@ -162,7 +162,9 @@ impl Show for ListNode { } .pack()) } +} +impl Finalize for ListNode { fn finalize( &self, _: Tracked, @@ -312,14 +314,14 @@ impl Label { ) -> SourceResult { Ok(match self { Self::Default => match kind { - LIST => TextNode('•'.into()).pack(), - ENUM => TextNode(format_eco!("{}.", number)).pack(), + LIST => TextNode::packed('•'), + ENUM => TextNode::packed(format_eco!("{}.", number)), DESC | _ => panic!("description lists don't have a label"), }, Self::Pattern(prefix, numbering, upper, suffix) => { let fmt = numbering.apply(number); let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() }; - TextNode(format_eco!("{}{}{}", prefix, mid, suffix)).pack() + TextNode::packed(format_eco!("{}{}{}", prefix, mid, suffix)) } Self::Content(content) => content.clone(), Self::Func(func, span) => { diff --git a/library/src/structure/reference.rs b/library/src/structure/reference.rs index 56f8b8e33..18f4eecb4 100644 --- a/library/src/structure/reference.rs +++ b/library/src/structure/reference.rs @@ -8,7 +8,14 @@ pub struct RefNode(pub EcoString); #[node(Show)] impl RefNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - Ok(Self(args.expect("label")?).pack()) + Ok(Self(args.expect("target")?).pack()) + } + + fn field(&self, name: &str) -> Option { + match name { + "target" => Some(Value::Str(self.0.clone().into())), + _ => None, + } } } @@ -17,14 +24,7 @@ impl Show for RefNode { Self(self.0.clone()).pack() } - fn field(&self, name: &str) -> Option { - match name { - "label" => Some(Value::Str(self.0.clone().into())), - _ => None, - } - } - - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { - Ok(TextNode(format_eco!("@{}", self.0)).pack()) + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { + Ok(TextNode::packed(format_eco!("@{}", self.0))) } } diff --git a/library/src/structure/table.rs b/library/src/structure/table.rs index 722f11e6a..fbf1c7c0f 100644 --- a/library/src/structure/table.rs +++ b/library/src/structure/table.rs @@ -12,7 +12,7 @@ pub struct TableNode { pub cells: Vec, } -#[node(Show)] +#[node(Show, Finalize)] impl TableNode { /// How to fill the cells. #[property(referenced)] @@ -46,6 +46,15 @@ impl TableNode { } .pack()) } + + fn field(&self, name: &str) -> Option { + match name { + "cells" => Some(Value::Array( + self.cells.iter().cloned().map(Value::Content).collect(), + )), + _ => None, + } + } } impl Show for TableNode { @@ -58,16 +67,7 @@ impl Show for TableNode { .pack() } - fn field(&self, name: &str) -> Option { - match name { - "cells" => Some(Value::Array( - self.cells.iter().cloned().map(Value::Content).collect(), - )), - _ => None, - } - } - - fn realize( + fn show( &self, world: Tracked, styles: StyleChain, @@ -106,7 +106,9 @@ impl Show for TableNode { } .pack()) } +} +impl Finalize for TableNode { fn finalize( &self, _: Tracked, diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs index 10f3db381..fa0f05a70 100644 --- a/library/src/text/deco.rs +++ b/library/src/text/deco.rs @@ -37,12 +37,6 @@ impl DecoNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } -} - -impl Show for DecoNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self(self.0.unguard(sel)).pack() - } fn field(&self, name: &str) -> Option { match name { @@ -50,12 +44,14 @@ impl Show for DecoNode { _ => None, } } +} - fn realize( - &self, - _: Tracked, - styles: StyleChain, - ) -> SourceResult { +impl Show for DecoNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self(self.0.unguard(sel)).pack() + } + + fn show(&self, _: Tracked, styles: StyleChain) -> SourceResult { Ok(self.0.clone().styled( TextNode::DECO, Decoration { @@ -81,6 +77,15 @@ pub(super) struct Decoration { pub evade: bool, } +impl Fold for Decoration { + type Output = Vec; + + fn fold(self, mut outer: Self::Output) -> Self::Output { + outer.insert(0, self); + outer + } +} + /// A kind of decorative line. pub type DecoLine = usize; diff --git a/library/src/text/link.rs b/library/src/text/link.rs index 82abe5cd4..4312559e9 100644 --- a/library/src/text/link.rs +++ b/library/src/text/link.rs @@ -17,7 +17,7 @@ impl LinkNode { } } -#[node(Show)] +#[node(Show, Finalize)] impl LinkNode { /// The fill color of text in the link. Just the surrounding text color /// if `auto`. @@ -33,16 +33,6 @@ impl LinkNode { }; Ok(Self { dest, body }.pack()) } -} - -impl Show for LinkNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { - dest: self.dest.clone(), - body: self.body.as_ref().map(|body| body.unguard(sel)), - } - .pack() - } fn field(&self, name: &str) -> Option { match name { @@ -57,25 +47,33 @@ impl Show for LinkNode { _ => None, } } +} - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { - Ok(self - .body - .clone() - .unwrap_or_else(|| match &self.dest { - Destination::Url(url) => { - let mut text = url.as_str(); - for prefix in ["mailto:", "tel:"] { - text = text.trim_start_matches(prefix); - } - let shorter = text.len() < url.len(); - TextNode(if shorter { text.into() } else { url.clone() }).pack() - } - Destination::Internal(_) => Content::empty(), - }) - .styled(TextNode::LINK, Some(self.dest.clone()))) +impl Show for LinkNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self { + dest: self.dest.clone(), + body: self.body.as_ref().map(|body| body.unguard(sel)), + } + .pack() } + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { + Ok(self.body.clone().unwrap_or_else(|| match &self.dest { + Destination::Url(url) => { + let mut text = url.as_str(); + for prefix in ["mailto:", "tel:"] { + text = text.trim_start_matches(prefix); + } + let shorter = text.len() < url.len(); + TextNode::packed(if shorter { text.into() } else { url.clone() }) + } + Destination::Internal(_) => Content::empty(), + })) + } +} + +impl Finalize for LinkNode { fn finalize( &self, _: Tracked, @@ -83,6 +81,8 @@ impl Show for LinkNode { mut realized: Content, ) -> SourceResult { let mut map = StyleMap::new(); + map.set(TextNode::LINK, Some(self.dest.clone())); + if let Smart::Custom(fill) = styles.get(Self::FILL) { map.set(TextNode::FILL, fill); } diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 6643f821c..86c6884a2 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -28,6 +28,13 @@ use crate::prelude::*; #[derive(Debug, Clone, Hash)] pub struct TextNode(pub EcoString); +impl TextNode { + /// Create a new packed text node. + pub fn packed(text: impl Into) -> Content { + Self(text.into()).pack() + } +} + #[node] impl TextNode { /// A prioritized sequence of font families. @@ -486,12 +493,6 @@ impl StrongNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } -} - -impl Show for StrongNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self(self.0.unguard(sel)).pack() - } fn field(&self, name: &str) -> Option { match name { @@ -499,8 +500,14 @@ impl Show for StrongNode { _ => None, } } +} - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { +impl Show for StrongNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self(self.0.unguard(sel)).pack() + } + + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { Ok(self.0.clone().styled(TextNode::BOLD, Toggle)) } } @@ -514,12 +521,6 @@ impl EmphNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } -} - -impl Show for EmphNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self(self.0.unguard(sel)).pack() - } fn field(&self, name: &str) -> Option { match name { @@ -527,8 +528,14 @@ impl Show for EmphNode { _ => None, } } +} - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { +impl Show for EmphNode { + fn unguard_parts(&self, sel: Selector) -> Content { + Self(self.0.unguard(sel)).pack() + } + + fn show(&self, _: Tracked, _: StyleChain) -> SourceResult { Ok(self.0.clone().styled(TextNode::ITALIC, Toggle)) } } @@ -544,12 +551,3 @@ impl Fold for Toggle { !outer } } - -impl Fold for Decoration { - type Output = Vec; - - fn fold(self, mut outer: Self::Output) -> Self::Output { - outer.insert(0, self); - outer - } -} diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs index 31f1517e3..5a98cf3bd 100644 --- a/library/src/text/raw.rs +++ b/library/src/text/raw.rs @@ -19,7 +19,7 @@ pub struct RawNode { pub block: bool, } -#[node(Show)] +#[node(Show, Finalize)] impl RawNode { /// The language to syntax-highlight in. #[property(referenced)] @@ -41,12 +41,6 @@ impl RawNode { } .pack()) } -} - -impl Show for RawNode { - fn unguard_parts(&self, _: Selector) -> Content { - Self { text: self.text.clone(), ..*self }.pack() - } fn field(&self, name: &str) -> Option { match name { @@ -55,12 +49,14 @@ impl Show for RawNode { _ => None, } } +} - fn realize( - &self, - _: Tracked, - styles: StyleChain, - ) -> SourceResult { +impl Show for RawNode { + fn unguard_parts(&self, _: Selector) -> Content { + Self { text: self.text.clone(), ..*self }.pack() + } + + fn show(&self, _: Tracked, styles: StyleChain) -> SourceResult { let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase()); let foreground = THEME .settings @@ -100,7 +96,7 @@ impl Show for RawNode { Content::sequence(seq) } else { - TextNode(self.text.clone()).pack() + TextNode::packed(self.text.clone()) }; if self.block { @@ -114,7 +110,9 @@ impl Show for RawNode { Ok(realized.styled_with_map(map)) } +} +impl Finalize for RawNode { fn finalize( &self, _: Tracked, @@ -134,7 +132,7 @@ impl Show for RawNode { /// Style a piece of text with a syntect style. fn styled(piece: &str, foreground: Paint, style: Style) -> Content { - let mut body = TextNode(piece.into()).pack(); + let mut body = TextNode::packed(piece); let paint = style.foreground.into(); if paint != foreground { diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs index 1117cc00b..0f654b5a9 100644 --- a/library/src/text/shift.rs +++ b/library/src/text/shift.rs @@ -33,12 +33,6 @@ impl ShiftNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } -} - -impl Show for ShiftNode { - fn unguard_parts(&self, _: Selector) -> Content { - Self(self.0.clone()).pack() - } fn field(&self, name: &str) -> Option { match name { @@ -46,8 +40,14 @@ impl Show for ShiftNode { _ => None, } } +} - fn realize( +impl Show for ShiftNode { + fn unguard_parts(&self, _: Selector) -> Content { + Self(self.0.clone()).pack() + } + + fn show( &self, world: Tracked, styles: StyleChain, @@ -56,7 +56,7 @@ impl Show for ShiftNode { if styles.get(Self::TYPOGRAPHIC) { if let Some(text) = search_text(&self.0, S) { if is_shapable(world, &text, styles) { - transformed = Some(TextNode(text).pack()); + transformed = Some(TextNode::packed(text)); } } }; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 8e44a8c35..eaa929dc8 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -47,6 +47,7 @@ fn expand_node( let mut properties = vec![]; let mut construct = None; let mut set = None; + let mut field = None; for item in std::mem::take(&mut impl_block.items) { match item { @@ -61,6 +62,7 @@ fn expand_node( match method.sig.ident.to_string().as_str() { "construct" => construct = Some(method), "set" => set = Some(method), + "field" => field = Some(method), _ => return Err(Error::new(method.span(), "unexpected method")), } } @@ -81,6 +83,14 @@ fn expand_node( let set = generate_set(&properties, set); + let field = field.unwrap_or_else(|| { + parse_quote! { + fn field(&self, name: &str) -> Option { + None + } + } + }); + let items: syn::punctuated::Punctuated = parse_quote! { #stream }; @@ -115,11 +125,13 @@ fn expand_node( impl<#params> model::Node for #self_ty { #construct #set - #vtable + #field fn id(&self) -> model::NodeId { model::NodeId::of::() } + + #vtable } #(#key_modules)* diff --git a/src/model/cast.rs b/src/model/cast.rs index cbb2952d6..7a466b72e 100644 --- a/src/model/cast.rs +++ b/src/model/cast.rs @@ -1,7 +1,7 @@ use std::num::NonZeroUsize; use std::str::FromStr; -use super::{Pattern, Regex, Value}; +use super::{Content, Pattern, Regex, Transform, Value}; use crate::diag::{with_alternative, StrResult}; use crate::font::{FontStretch, FontStyle, FontWeight}; use crate::frame::{Destination, Lang, Location, Region}; @@ -189,6 +189,15 @@ castable! { @regex: Regex => Self::Regex(regex.clone()), } +castable! { + Transform, + Expected: "content or function", + Value::None => Self::Content(Content::empty()), + Value::Str(text) => Self::Content(item!(text)(text.into())), + Value::Content(content) => Self::Content(content), + Value::Func(func) => Self::Func(func), +} + dynamic! { Dir: "direction", } diff --git a/src/model/content.rs b/src/model/content.rs index 7b09c697d..0257f4da5 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -5,13 +5,14 @@ use std::iter::{self, Sum}; use std::ops::{Add, AddAssign}; use std::sync::Arc; +use comemo::Tracked; use siphasher::sip128::{Hasher128, SipHasher}; use typst_macros::node; -use super::{Args, Key, Property, Selector, StyleEntry, StyleMap, Vm}; -use crate as typst; +use super::{Args, Key, Property, Recipe, Selector, StyleEntry, StyleMap, Value, Vm}; use crate::diag::{SourceResult, StrResult}; -use crate::util::{EcoString, ReadableTypeId}; +use crate::util::ReadableTypeId; +use crate::World; /// Composable representation of styled content. /// @@ -27,11 +28,6 @@ impl Content { SequenceNode(vec![]).pack() } - /// Create content from a string of text. - pub fn text(text: impl Into) -> Self { - item!(text)(text.into()) - } - /// Create a new sequence node from multiples nodes. pub fn sequence(seq: Vec) -> Self { match seq.as_slice() { @@ -65,6 +61,11 @@ impl Content { Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::() } + /// Access a field on this content. + pub fn field(&self, name: &str) -> Option { + self.0.field(name) + } + /// Whether this content has the given capability. pub fn has(&self) -> bool where @@ -97,6 +98,19 @@ impl Content { self.styled_with_entry(StyleEntry::Property(Property::new(key, value))) } + /// Style this content with a recipe, eagerly applying it if possible. + pub fn styled_with_recipe( + self, + world: Tracked, + recipe: Recipe, + ) -> SourceResult { + if recipe.pattern.is_none() { + recipe.transform.apply(world, recipe.span, || Value::Content(self)) + } else { + Ok(self.styled_with_entry(StyleEntry::Recipe(recipe))) + } + } + /// Style this content with a style entry. pub fn styled_with_entry(mut self, entry: StyleEntry) -> Self { if let Some(styled) = self.try_downcast_mut::() { @@ -242,6 +256,9 @@ pub trait Node: 'static { where Self: Sized; + /// Access a field on this node. + fn field(&self, name: &str) -> Option; + /// A unique identifier of the node type. fn id(&self) -> NodeId; diff --git a/src/model/eval.rs b/src/model/eval.rs index 16e66818d..fd43c4c31 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -7,7 +7,7 @@ use unicode_segmentation::UnicodeSegmentation; use super::{ methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func, - Pattern, Recipe, Scope, Scopes, Show, StyleEntry, StyleMap, Value, Vm, + Pattern, Recipe, Scope, Scopes, StyleMap, Transform, Value, Vm, }; use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint}; use crate::geom::{Abs, Angle, Em, Fr, Ratio}; @@ -133,12 +133,8 @@ fn eval_markup( break; } - eval_markup(vm, nodes)?.styled_with_entry(StyleEntry::Recipe(recipe)) - } - ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => { let tail = eval_markup(vm, nodes)?; - vm.scopes.top.define(wrap.binding().take(), tail); - wrap.body().eval(vm)?.display(vm.world) + tail.styled_with_recipe(vm.world, recipe)? } _ => node.eval(vm)?, }); @@ -408,7 +404,6 @@ impl Eval for ast::Expr { Self::Let(v) => v.eval(vm), Self::Set(_) => bail!(forbidden("set")), Self::Show(_) => bail!(forbidden("show")), - Self::Wrap(_) => bail!(forbidden("wrap")), Self::Conditional(v) => v.eval(vm), Self::While(v) => v.eval(vm), Self::For(v) => v.eval(vm), @@ -484,18 +479,12 @@ fn eval_code( } ast::Expr::Show(show) => { let recipe = show.eval(vm)?; - let entry = StyleEntry::Recipe(recipe); if vm.flow.is_some() { break; } let tail = eval_code(vm, exprs)?.display(vm.world); - Value::Content(tail.styled_with_entry(entry)) - } - ast::Expr::Wrap(wrap) => { - let tail = eval_code(vm, exprs)?; - vm.scopes.top.define(wrap.binding().take(), tail); - wrap.body().eval(vm)? + Value::Content(tail.styled_with_recipe(vm.world, recipe)?) } _ => expr.eval(vm)?, }; @@ -671,9 +660,8 @@ impl Eval for ast::FieldAccess { Ok(match object { Value::Dict(dict) => dict.get(&field).at(span)?.clone(), - Value::Content(node) => node - .to::() - .and_then(|node| node.field(&field)) + Value::Content(content) => content + .field(&field) .ok_or_else(|| format!("unknown field {field:?}")) .at(span)?, v => bail!(self.target().span(), "cannot access field on {}", v.type_name()), @@ -838,6 +826,11 @@ impl Eval for ast::SetRule { type Output = StyleMap; fn eval(&self, vm: &mut Vm) -> SourceResult { + if let Some(condition) = self.condition() { + if !condition.eval(vm)?.cast::().at(condition.span())? { + return Ok(StyleMap::new()); + } + } let target = self.target(); let target = target.eval(vm)?.cast::().at(target.span())?; let args = self.args().eval(vm)?; @@ -849,36 +842,16 @@ impl Eval for ast::ShowRule { type Output = Recipe; fn eval(&self, vm: &mut Vm) -> SourceResult { - // Evaluate the target function. - let pattern = self.pattern(); - let pattern = pattern.eval(vm)?.cast::().at(pattern.span())?; + let pattern = self + .pattern() + .map(|pattern| pattern.eval(vm)?.cast::().at(pattern.span())) + .transpose()?; - // Collect captured variables. - let captured = { - let mut visitor = CapturesVisitor::new(&vm.scopes); - visitor.visit(self.as_untyped()); - visitor.finish() - }; + let transform = self.transform(); + let span = transform.span(); + let transform = transform.eval(vm)?.cast::().at(span)?; - // Define parameters. - let mut params = vec![]; - if let Some(binding) = self.binding() { - params.push((binding.take(), None)); - } - - // Define the recipe function. - let body = self.body(); - let span = body.span(); - let func = Func::from_closure(Closure { - location: vm.location, - name: None, - captured, - params, - sink: None, - body, - }); - - Ok(Recipe { pattern, func: Spanned::new(func, span) }) + Ok(Recipe { span, pattern, transform }) } } @@ -1066,7 +1039,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult { Ok(module) } -impl Eval for ast::BreakStmt { +impl Eval for ast::LoopBreak { type Output = Value; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -1077,7 +1050,7 @@ impl Eval for ast::BreakStmt { } } -impl Eval for ast::ContinueStmt { +impl Eval for ast::LoopContinue { type Output = Value; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -1088,7 +1061,7 @@ impl Eval for ast::ContinueStmt { } } -impl Eval for ast::ReturnStmt { +impl Eval for ast::FuncReturn { type Output = Value; fn eval(&self, vm: &mut Vm) -> SourceResult { diff --git a/src/model/func.rs b/src/model/func.rs index 456b6aa68..8cedb158f 100644 --- a/src/model/func.rs +++ b/src/model/func.rs @@ -310,16 +310,6 @@ impl<'a> CapturesVisitor<'a> { self.bind(expr.binding()); } - // A show rule contains a binding, but that binding is only active - // after the target has been evaluated. - Some(ast::Expr::Show(show)) => { - self.visit(show.pattern().as_untyped()); - if let Some(binding) = show.binding() { - self.bind(binding); - } - self.visit(show.body().as_untyped()); - } - // A for loop contains one or two bindings in its pattern. These are // active after the iterable is evaluated but before the body is // evaluated. @@ -391,9 +381,9 @@ mod tests { test("{(x, y: x + z) => x + y}", &["x", "z"]); // Show rule. - test("#show x: y as x", &["y"]); - test("#show x: y as x + z", &["y", "z"]); - test("#show x: x as x", &["x"]); + test("#show y: x => x", &["y"]); + test("#show y: x => x + z", &["y", "z"]); + test("#show x: x => x", &["x"]); // For loop. test("#for x in y { x + z }", &["y", "z"]); diff --git a/src/model/items.rs b/src/model/items.rs index 164d9602a..e9c23c264 100644 --- a/src/model/items.rs +++ b/src/model/items.rs @@ -48,11 +48,11 @@ pub struct LangItems { pub em: fn(StyleChain) -> Abs, /// Access the text direction. pub dir: fn(StyleChain) -> Dir, - /// A space. + /// Whitespace. pub space: fn() -> Content, - /// A forced line break. + /// A forced line break: `\`. pub linebreak: fn(justify: bool) -> Content, - /// Plain text. + /// Plain text without markup. pub text: fn(text: EcoString) -> Content, /// A smart quote: `'` or `"`. pub smart_quote: fn(double: bool) -> Content, @@ -72,18 +72,18 @@ pub struct LangItems { pub heading: fn(level: NonZeroUsize, body: Content) -> Content, /// An item in an unordered list: `- ...`. pub list_item: fn(body: Content) -> Content, - /// An item in an enumeration (ordered list): `1. ...`. + /// An item in an enumeration (ordered list): `+ ...` or `1. ...`. pub enum_item: fn(number: Option, body: Content) -> Content, /// An item in a description list: `/ Term: Details`. pub desc_item: fn(term: Content, body: Content) -> Content, - /// A math formula: `$x$`, `$ x^2 $`. + /// A mathematical formula: `$x$`, `$ x^2 $`. pub math: fn(children: Vec, display: bool) -> Content, - /// A atom in a formula: `x`, `+`, `12`. + /// An atom in a formula: `x`, `+`, `12`. pub math_atom: fn(atom: EcoString) -> Content, - /// A base with an optional sub- and superscript in a formula: `a_1^2`. + /// A base with optional sub- and superscripts in a formula: `a_1^2`. pub math_script: fn(base: Content, sub: Option, sup: Option) -> Content, - /// A fraction in a formula: `x/2` + /// A fraction in a formula: `x/2`. pub math_frac: fn(num: Content, denom: Content) -> Content, /// An alignment indicator in a formula: `&`, `&&`. pub math_align: fn(count: usize) -> Content, diff --git a/src/model/ops.rs b/src/model/ops.rs index 9d55fa63b..0110fb96c 100644 --- a/src/model/ops.rs +++ b/src/model/ops.rs @@ -19,8 +19,8 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult { (a, None) => a, (None, b) => b, (Str(a), Str(b)) => Str(a + b), - (Str(a), Content(b)) => Content(super::Content::text(a) + b), - (Content(a), Str(b)) => Content(a + super::Content::text(b)), + (Str(a), Content(b)) => Content(item!(text)(a.into()) + b), + (Content(a), Str(b)) => Content(a + item!(text)(b.into())), (Content(a), Content(b)) => Content(a + b), (Array(a), Array(b)) => Array(a + b), (Dict(a), Dict(b)) => Dict(a + b), @@ -85,8 +85,8 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult { (Str(a), Str(b)) => Str(a + b), (Content(a), Content(b)) => Content(a + b), - (Content(a), Str(b)) => Content(a + super::Content::text(b)), - (Str(a), Content(b)) => Content(super::Content::text(a) + b), + (Content(a), Str(b)) => Content(a + item!(text)(b.into())), + (Str(a), Content(b)) => Content(item!(text)(a.into()) + b), (Array(a), Array(b)) => Array(a + b), (Dict(a), Dict(b)) => Dict(a + b), diff --git a/src/model/styles.rs b/src/model/styles.rs index 9463e55e5..3800490b8 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -12,7 +12,7 @@ use crate::diag::SourceResult; use crate::geom::{ Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides, }; -use crate::syntax::Spanned; +use crate::syntax::Span; use crate::util::ReadableTypeId; use crate::World; @@ -283,17 +283,17 @@ impl<'a> StyleChain<'a> { .unguard_parts(sel) .to::() .unwrap() - .realize(world, self)?; + .show(world, self)?; realized = Some(content.styled_with_entry(StyleEntry::Guard(sel))); } } // Finalize only if guarding didn't stop any recipe. if !guarded { - if let Some(content) = realized { - realized = Some( - node.to::().unwrap().finalize(world, self, content)?, - ); + if let Some(node) = node.to::() { + if let Some(content) = realized { + realized = Some(node.finalize(world, self, content)?); + } } } } @@ -974,18 +974,20 @@ impl Fold for PartialStroke { /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] pub struct Recipe { - /// The patterns to customize. - pub pattern: Pattern, - /// The function that defines the recipe. - pub func: Spanned, + /// The span errors are reported with. + pub span: Span, + /// The pattern that the rule applies to. + pub pattern: Option, + /// The transformation to perform on the match. + pub transform: Transform, } impl Recipe { /// Whether the recipe is applicable to the target. pub fn applicable(&self, target: Target) -> bool { match (&self.pattern, target) { - (Pattern::Node(id), Target::Node(node)) => *id == node.id(), - (Pattern::Regex(_), Target::Text(_)) => true, + (Some(Pattern::Node(id)), Target::Node(node)) => *id == node.id(), + (Some(Pattern::Regex(_)), Target::Text(_)) => true, _ => false, } } @@ -998,12 +1000,13 @@ impl Recipe { target: Target, ) -> SourceResult> { let content = match (target, &self.pattern) { - (Target::Node(node), &Pattern::Node(id)) if node.id() == id => { - let node = node.to::().unwrap().unguard_parts(sel); - self.call(world, || Value::Content(node))? + (Target::Node(node), Some(Pattern::Node(id))) if node.id() == *id => { + self.transform.apply(world, self.span, || { + Value::Content(node.to::().unwrap().unguard_parts(sel)) + })? } - (Target::Text(text), Pattern::Regex(regex)) => { + (Target::Text(text), Some(Pattern::Regex(regex))) => { let make = world.config().items.text; let mut result = vec![]; let mut cursor = 0; @@ -1014,7 +1017,10 @@ impl Recipe { result.push(make(text[cursor..start].into())); } - result.push(self.call(world, || Value::Str(mat.as_str().into()))?); + let transformed = self + .transform + .apply(world, self.span, || Value::Str(mat.as_str().into()))?; + result.push(transformed); cursor = mat.end(); } @@ -1035,24 +1041,10 @@ impl Recipe { Ok(Some(content.styled_with_entry(StyleEntry::Guard(sel)))) } - /// Call the recipe function, with the argument if desired. - fn call(&self, world: Tracked, arg: F) -> SourceResult - where - F: FnOnce() -> Value, - { - let args = if self.func.v.argc() == Some(0) { - Args::new(self.func.span, []) - } else { - Args::new(self.func.span, [arg()]) - }; - - Ok(self.func.v.call_detached(world, args)?.display(world)) - } - /// Whether this recipe is for the given node. pub fn is_of(&self, node: NodeId) -> bool { match self.pattern { - Pattern::Node(id) => id == node, + Some(Pattern::Node(id)) => id == node, _ => false, } } @@ -1060,7 +1052,7 @@ impl Recipe { impl Debug for Recipe { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Recipe matching {:?} from {:?}", self.pattern, self.func.span) + write!(f, "Recipe matching {:?}", self.pattern) } } @@ -1080,6 +1072,36 @@ impl Pattern { } } +/// A show rule transformation that can be applied to a match. +#[derive(Debug, Clone, PartialEq, Hash)] +pub enum Transform { + /// Replacement content. + Content(Content), + /// A function to apply to the match. + Func(Func), +} + +impl Transform { + /// Apply the transform. + pub fn apply( + &self, + world: Tracked, + span: Span, + arg: F, + ) -> SourceResult + where + F: FnOnce() -> Value, + { + match self { + Transform::Content(content) => Ok(content.clone()), + Transform::Func(func) => { + let args = Args::new(span, [arg()]); + Ok(func.call_detached(world, args)?.display(world)) + } + } + } +} + /// A target for a show rule recipe. #[derive(Debug, Copy, Clone, PartialEq)] pub enum Target<'a> { @@ -1104,30 +1126,25 @@ pub trait Show: 'static + Sync + Send { /// Unguard nested content against recursive show rules. fn unguard_parts(&self, sel: Selector) -> Content; - /// Access a field on this node. - fn field(&self, name: &str) -> Option; - /// The base recipe for this node that is executed if there is no /// user-defined show rule. - fn realize( + fn show( &self, world: Tracked, styles: StyleChain, ) -> SourceResult; +} +/// Post-process a node after it was realized. +#[capability] +pub trait Finalize: 'static + Sync + Send { /// Finalize this node given the realization of a base or user recipe. Use /// this for effects that should work even in the face of a user-defined - /// show rule, for example: - /// - Application of general settable properties - /// - /// Defaults to just the realized content. - #[allow(unused_variables)] + /// show rule, for example the linking behaviour of a link node. fn finalize( &self, world: Tracked, styles: StyleChain, realized: Content, - ) -> SourceResult { - Ok(realized) - } + ) -> SourceResult; } diff --git a/src/model/value.rs b/src/model/value.rs index 825e6f552..9e6968fa3 100644 --- a/src/model/value.rs +++ b/src/model/value.rs @@ -381,7 +381,7 @@ primitive! { Str: "string", Str } primitive! { Content: "content", Content, None => Content::empty(), - Str(text) => Content::text(text) + Str(text) => item!(text)(text.into()) } primitive! { Array: "array", Array } primitive! { Dict: "dictionary", Dict } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 61b8f0e6b..1b0e8985c 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -63,11 +63,11 @@ impl Markup { /// A single piece of markup. #[derive(Debug, Clone, PartialEq)] pub enum MarkupNode { - /// Whitespace containing less than two newlines. + /// Whitespace. Space(Space), - /// A forced line break. + /// A forced line break: `\`. Linebreak(Linebreak), - /// Plain text. + /// Plain text without markup. Text(Text), /// An escape sequence: `\#`, `\u{1F5FA}`. Escape(Escape), @@ -76,9 +76,9 @@ pub enum MarkupNode { Shorthand(Shorthand), /// A smart quote: `'` or `"`. SmartQuote(SmartQuote), - /// Strong markup: `*Strong*`. + /// Strong content: `*Strong*`. Strong(Strong), - /// Emphasized markup: `_Emphasized_`. + /// Emphasized content: `_Emphasized_`. Emph(Emph), /// A raw block with optional syntax highlighting: `` `...` ``. Raw(Raw), @@ -171,7 +171,7 @@ node! { } node! { - /// Plain text. + /// Plain text without markup. Text } @@ -367,12 +367,12 @@ impl ListItem { } node! { - /// An item in an enumeration (ordered list): `1. ...`. + /// An item in an enumeration (ordered list): `+ ...` or `1. ...`. EnumItem } impl EnumItem { - /// The number, if any. + /// The explicit numbering, if any: `23.`. pub fn number(&self) -> Option { self.0.children().find_map(|node| match node.kind() { NodeKind::EnumNumbering(num) => Some(*num), @@ -434,9 +434,9 @@ pub enum MathNode { Linebreak(Linebreak), /// An escape sequence: `\#`, `\u{1F5FA}`. Escape(Escape), - /// A atom: `x`, `+`, `12`. + /// An atom: `x`, `+`, `12`. Atom(Atom), - /// A base with an optional sub- and superscript: `a_1^2`. + /// A base with optional sub- and superscripts: `a_1^2`. Script(Script), /// A fraction: `x/2`. Frac(Frac), @@ -565,9 +565,9 @@ pub enum Expr { Content(ContentBlock), /// A grouped expression: `(1 + 2)`. Parenthesized(Parenthesized), - /// An array expression: `(1, "hi", 12cm)`. + /// An array: `(1, "hi", 12cm)`. Array(Array), - /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. + /// A dictionary: `(thickness: 3pt, pattern: dashed)`. Dict(Dict), /// A unary operation: `-x`. Unary(Unary), @@ -579,16 +579,14 @@ pub enum Expr { FuncCall(FuncCall), /// An invocation of a method: `array.push(v)`. MethodCall(MethodCall), - /// A closure expression: `(x, y) => z`. + /// A closure: `(x, y) => z`. Closure(Closure), /// A let binding: `let x = 1`. Let(LetBinding), /// A set rule: `set text(...)`. Set(SetRule), - /// A show rule: `show node: heading as [*{nody.body}*]`. + /// A show rule: `show heading: it => [*{it.body}*]`. Show(ShowRule), - /// A wrap rule: `wrap body in columns(2, body)`. - Wrap(WrapRule), /// An if-else conditional: `if x { y } else { z }`. Conditional(Conditional), /// A while loop: `while x { y }`. @@ -599,12 +597,12 @@ pub enum Expr { Import(ModuleImport), /// A module include: `include "chapter1.typ"`. Include(ModuleInclude), - /// A break statement: `break`. - Break(BreakStmt), - /// A continue statement: `continue`. - Continue(ContinueStmt), - /// A return statement: `return`, `return x + 1`. - Return(ReturnStmt), + /// A break from a loop: `break`. + Break(LoopBreak), + /// A continue in a loop: `continue`. + Continue(LoopContinue), + /// A return from a function: `return`, `return x + 1`. + Return(FuncReturn), } impl TypedNode for Expr { @@ -625,15 +623,14 @@ impl TypedNode for Expr { NodeKind::LetBinding => node.cast().map(Self::Let), NodeKind::SetRule => node.cast().map(Self::Set), NodeKind::ShowRule => node.cast().map(Self::Show), - NodeKind::WrapRule => node.cast().map(Self::Wrap), NodeKind::Conditional => node.cast().map(Self::Conditional), NodeKind::WhileLoop => node.cast().map(Self::While), NodeKind::ForLoop => node.cast().map(Self::For), NodeKind::ModuleImport => node.cast().map(Self::Import), NodeKind::ModuleInclude => node.cast().map(Self::Include), - NodeKind::BreakStmt => node.cast().map(Self::Break), - NodeKind::ContinueStmt => node.cast().map(Self::Continue), - NodeKind::ReturnStmt => node.cast().map(Self::Return), + NodeKind::LoopBreak => node.cast().map(Self::Break), + NodeKind::LoopContinue => node.cast().map(Self::Continue), + NodeKind::FuncReturn => node.cast().map(Self::Return), _ => node.cast().map(Self::Lit), } } @@ -656,7 +653,6 @@ impl TypedNode for Expr { Self::Let(v) => v.as_untyped(), Self::Set(v) => v.as_untyped(), Self::Show(v) => v.as_untyped(), - Self::Wrap(v) => v.as_untyped(), Self::Conditional(v) => v.as_untyped(), Self::While(v) => v.as_untyped(), Self::For(v) => v.as_untyped(), @@ -679,7 +675,6 @@ impl Expr { | Self::Let(_) | Self::Set(_) | Self::Show(_) - | Self::Wrap(_) | Self::Conditional(_) | Self::While(_) | Self::For(_) @@ -723,15 +718,15 @@ pub enum LitKind { None, /// The auto literal: `auto`. Auto, - /// A boolean literal: `true`, `false`. + /// A boolean: `true`, `false`. Bool(bool), - /// An integer literal: `120`. + /// An integer: `120`. Int(i64), - /// A floating-point literal: `1.2`, `10e-4`. + /// A floating-point number: `1.2`, `10e-4`. Float(f64), - /// A numeric literal with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`. + /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`. Numeric(f64, Unit), - /// A string literal: `"hello!"`. + /// A quoted string: `"..."`. Str(EcoString), } @@ -760,7 +755,7 @@ impl ContentBlock { } node! { - /// A parenthesized expression: `(1 + 2)`. + /// A grouped expression: `(1 + 2)`. Parenthesized } @@ -853,7 +848,7 @@ impl TypedNode for DictItem { } node! { - /// A pair of a name and an expression: `thickness: 3pt`. + /// A named pair: `thickness: 3pt`. Named } @@ -870,7 +865,7 @@ impl Named { } node! { - /// A pair of a string key and an expression: `"spacy key": true`. + /// A keyed pair: `"spacy key": true`. Keyed } @@ -1204,7 +1199,7 @@ impl MethodCall { } node! { - /// The arguments to a function: `12, draw: false`. + /// A function call's argument list: `(12pt, y)`. Args } @@ -1245,7 +1240,7 @@ impl TypedNode for Arg { } node! { - /// A closure expression: `(x, y) => z`. + /// A closure: `(x, y) => z`. Closure } @@ -1347,52 +1342,34 @@ impl SetRule { pub fn args(&self) -> Args { self.0.cast_last_child().expect("set rule is missing argument list") } + + /// A condition under which the set rule applies. + pub fn condition(&self) -> Option { + self.0 + .children() + .skip_while(|child| child.kind() != &NodeKind::If) + .find_map(SyntaxNode::cast) + } } node! { - /// A show rule: `show node: heading as [*{nody.body}*]`. + /// A show rule: `show heading: it => [*{it.body}*]`. ShowRule } impl ShowRule { - /// The binding to assign to. - pub fn binding(&self) -> Option { - let mut children = self.0.children(); - children - .find_map(SyntaxNode::cast) - .filter(|_| children.any(|child| child.kind() == &NodeKind::Colon)) - } - /// The pattern that this rule matches. - pub fn pattern(&self) -> Expr { + pub fn pattern(&self) -> Option { self.0 .children() .rev() - .skip_while(|child| child.kind() != &NodeKind::As) + .skip_while(|child| child.kind() != &NodeKind::Colon) .find_map(SyntaxNode::cast) - .expect("show rule is missing pattern") } - /// The expression that realizes the node. - pub fn body(&self) -> Expr { - self.0.cast_last_child().expect("show rule is missing body") - } -} - -node! { - /// A wrap rule: `wrap body in columns(2, body)`. - WrapRule -} - -impl WrapRule { - /// The binding to assign the remaining markup to. - pub fn binding(&self) -> Ident { - self.0.cast_first_child().expect("wrap rule is missing binding") - } - - /// The expression to evaluate. - pub fn body(&self) -> Expr { - self.0.cast_last_child().expect("wrap rule is missing body") + /// The transformation recipe. + pub fn transform(&self) -> Expr { + self.0.cast_last_child().expect("show rule is missing transform") } } @@ -1462,7 +1439,7 @@ impl ForLoop { } node! { - /// A for-in loop: `for x in y { z }`. + /// A for loop's destructuring pattern: `x` or `x, y`. ForPattern } @@ -1533,21 +1510,21 @@ impl ModuleInclude { } node! { - /// A break expression: `break`. - BreakStmt + /// A break from a loop: `break`. + LoopBreak } node! { - /// A continue expression: `continue`. - ContinueStmt + /// A continue in a loop: `continue`. + LoopContinue } node! { - /// A return expression: `return`, `return x + 1`. - ReturnStmt + /// A return from a function: `return`, `return x + 1`. + FuncReturn } -impl ReturnStmt { +impl FuncReturn { /// The expression to return. pub fn body(&self) -> Option { self.0.cast_last_child() @@ -1555,7 +1532,7 @@ impl ReturnStmt { } node! { - /// An identifier. + /// An identifier: `it`. Ident } diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs index d5345fab9..34cce4c0d 100644 --- a/src/syntax/highlight.rs +++ b/src/syntax/highlight.rs @@ -257,7 +257,6 @@ impl Category { NodeKind::Let => Some(Category::Keyword), NodeKind::Set => Some(Category::Keyword), NodeKind::Show => Some(Category::Keyword), - NodeKind::Wrap => Some(Category::Keyword), NodeKind::If => Some(Category::Keyword), NodeKind::Else => Some(Category::Keyword), NodeKind::For => Some(Category::Keyword), @@ -269,7 +268,6 @@ impl Category { NodeKind::Import => Some(Category::Keyword), NodeKind::Include => Some(Category::Keyword), NodeKind::From => Some(Category::Keyword), - NodeKind::As => Some(Category::Keyword), NodeKind::Markup { .. } => match parent.kind() { NodeKind::DescItem @@ -316,8 +314,7 @@ impl Category { if parent .children() .rev() - .skip_while(|child| child.kind() != &NodeKind::As) - .take_while(|child| child.kind() != &NodeKind::Colon) + .skip_while(|child| child.kind() != &NodeKind::Colon) .find(|c| matches!(c.kind(), NodeKind::Ident(_))) .map_or(false, |ident| std::ptr::eq(ident, child)) => { @@ -349,7 +346,6 @@ impl Category { NodeKind::LetBinding => None, NodeKind::SetRule => None, NodeKind::ShowRule => None, - NodeKind::WrapRule => None, NodeKind::Conditional => None, NodeKind::WhileLoop => None, NodeKind::ForLoop => None, @@ -357,9 +353,9 @@ impl Category { NodeKind::ModuleImport => None, NodeKind::ImportItems => None, NodeKind::ModuleInclude => None, - NodeKind::BreakStmt => None, - NodeKind::ContinueStmt => None, - NodeKind::ReturnStmt => None, + NodeKind::LoopBreak => None, + NodeKind::LoopContinue => None, + NodeKind::FuncReturn => None, NodeKind::Error(_, _) => Some(Category::Error), } diff --git a/src/syntax/kind.rs b/src/syntax/kind.rs index 1568693ba..1282b592f 100644 --- a/src/syntax/kind.rs +++ b/src/syntax/kind.rs @@ -106,8 +106,6 @@ pub enum NodeKind { Set, /// The `show` keyword. Show, - /// The `wrap` keyword. - Wrap, /// The `if` keyword. If, /// The `else` keyword. @@ -130,8 +128,6 @@ pub enum NodeKind { Include, /// The `from` keyword. From, - /// The `as` keyword. - As, /// Markup of which all lines must have a minimal indentation. /// @@ -139,7 +135,7 @@ pub enum NodeKind { /// started, but to the right of which column all markup elements must be, /// so it is zero except inside indent-aware constructs like lists. Markup { min_indent: usize }, - /// Consecutive text without markup. + /// Plain text without markup. Text(EcoString), /// A forced line break: `\`. Linebreak, @@ -150,9 +146,9 @@ pub enum NodeKind { Shorthand(char), /// A smart quote: `'` or `"`. SmartQuote { double: bool }, - /// Strong markup: `*Strong*`. + /// Strong content: `*Strong*`. Strong, - /// Emphasized markup: `_Emphasized_`. + /// Emphasized content: `_Emphasized_`. Emph, /// A raw block with optional syntax highlighting: `` `...` ``. Raw(Arc), @@ -164,26 +160,26 @@ pub enum NodeKind { Ref(EcoString), /// A section heading: `= Introduction`. Heading, - /// An item of an unordered list: `- ...`. + /// An item in an unordered list: `- ...`. ListItem, - /// An item of an enumeration (ordered list): `+ ...` or `1. ...`. + /// An item in an enumeration (ordered list): `+ ...` or `1. ...`. EnumItem, /// An explicit enumeration numbering: `23.`. EnumNumbering(usize), - /// An item of a description list: `/ Term: Details. + /// An item in a description list: `/ Term: Details`. DescItem, - /// A math formula: `$x$`, `$ x^2 $`. + /// A mathematical formula: `$x$`, `$ x^2 $`. Math, - /// An atom in a math formula: `x`, `+`, `12`. + /// An atom in a formula: `x`, `+`, `12`. Atom(EcoString), - /// A base with optional sub- and superscript in a math formula: `a_1^2`. + /// A base with optional sub- and superscripts in a formula: `a_1^2`. Script, - /// A fraction in a math formula: `x/2`. + /// A fraction in a formula: `x/2`. Frac, - /// An alignment indicator in a math formula: `&`, `&&`. + /// An alignment indicator in a formula: `&`, `&&`. Align, - /// An identifier: `center`. + /// An identifier: `it`. Ident(EcoString), /// A boolean: `true`, `false`. Bool(bool), @@ -219,9 +215,9 @@ pub enum NodeKind { FuncCall, /// An invocation of a method: `array.push(v)`. MethodCall, - /// A function call's argument list: `(x, y)`. + /// A function call's argument list: `(12pt, y)`. Args, - /// Spreaded arguments or a argument sink: `..x`. + /// Spreaded arguments or an argument sink: `..x`. Spread, /// A closure: `(x, y) => z`. Closure, @@ -231,15 +227,13 @@ pub enum NodeKind { LetBinding, /// A set rule: `set text(...)`. SetRule, - /// A show rule: `show node: heading as [*{nody.body}*]`. + /// A show rule: `show heading: it => [*{it.body}*]`. ShowRule, - /// A wrap rule: `wrap body in columns(2, body)`. - WrapRule, /// An if-else conditional: `if x { y } else { z }`. Conditional, - /// A while loop: `while x { ... }`. + /// A while loop: `while x { y }`. WhileLoop, - /// A for loop: `for x in y { ... }`. + /// A for loop: `for x in y { z }`. ForLoop, /// A for loop's destructuring pattern: `x` or `x, y`. ForPattern, @@ -249,12 +243,12 @@ pub enum NodeKind { ImportItems, /// A module include: `include "chapter1.typ"`. ModuleInclude, - /// A break statement: `break`. - BreakStmt, - /// A continue statement: `continue`. - ContinueStmt, - /// A return statement: `return x + 1`. - ReturnStmt, + /// A break from a loop: `break`. + LoopBreak, + /// A continue in a loop: `continue`. + LoopContinue, + /// A return from a function: `return`, `return x + 1`. + FuncReturn, /// An invalid sequence of characters. Error(ErrorPos, EcoString), @@ -367,7 +361,6 @@ impl NodeKind { Self::Let => "keyword `let`", Self::Set => "keyword `set`", Self::Show => "keyword `show`", - Self::Wrap => "keyword `wrap`", Self::If => "keyword `if`", Self::Else => "keyword `else`", Self::For => "keyword `for`", @@ -379,7 +372,6 @@ impl NodeKind { Self::Import => "keyword `import`", Self::Include => "keyword `include`", Self::From => "keyword `from`", - Self::As => "keyword `as`", Self::Markup { .. } => "markup", Self::Text(_) => "text", Self::Linebreak => "linebreak", @@ -426,7 +418,6 @@ impl NodeKind { Self::LetBinding => "`let` expression", Self::SetRule => "`set` expression", Self::ShowRule => "`show` expression", - Self::WrapRule => "`wrap` expression", Self::Conditional => "`if` expression", Self::WhileLoop => "while-loop expression", Self::ForLoop => "for-loop expression", @@ -434,9 +425,9 @@ impl NodeKind { Self::ModuleImport => "`import` expression", Self::ImportItems => "import items", Self::ModuleInclude => "`include` expression", - Self::BreakStmt => "`break` expression", - Self::ContinueStmt => "`continue` expression", - Self::ReturnStmt => "`return` expression", + Self::LoopBreak => "`break` expression", + Self::LoopContinue => "`continue` expression", + Self::FuncReturn => "`return` expression", Self::Error(_, _) => "syntax error", } } @@ -488,7 +479,6 @@ impl Hash for NodeKind { Self::Let => {} Self::Set => {} Self::Show => {} - Self::Wrap => {} Self::If => {} Self::Else => {} Self::For => {} @@ -500,7 +490,6 @@ impl Hash for NodeKind { Self::Import => {} Self::Include => {} Self::From => {} - Self::As => {} Self::Markup { min_indent } => min_indent.hash(state), Self::Text(s) => s.hash(state), Self::Linebreak => {} @@ -548,7 +537,6 @@ impl Hash for NodeKind { Self::LetBinding => {} Self::SetRule => {} Self::ShowRule => {} - Self::WrapRule => {} Self::Conditional => {} Self::WhileLoop => {} Self::ForLoop => {} @@ -556,9 +544,9 @@ impl Hash for NodeKind { Self::ModuleImport => {} Self::ImportItems => {} Self::ModuleInclude => {} - Self::BreakStmt => {} - Self::ContinueStmt => {} - Self::ReturnStmt => {} + Self::LoopBreak => {} + Self::LoopContinue => {} + Self::FuncReturn => {} Self::Error(pos, msg) => (pos, msg).hash(state), } } diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index fecc527c7..acf76b7e5 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -208,7 +208,6 @@ where }); } -/// Parse a markup node. fn markup_node(p: &mut Parser, at_start: &mut bool) { let Some(token) = p.peek() else { return }; match token { @@ -245,10 +244,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { NodeKind::Eq => heading(p, *at_start), // Lists. - NodeKind::Minus => list_node(p, *at_start), - NodeKind::Plus | NodeKind::EnumNumbering(_) => enum_node(p, *at_start), + NodeKind::Minus => list_item(p, *at_start), + NodeKind::Plus | NodeKind::EnumNumbering(_) => enum_item(p, *at_start), NodeKind::Slash => { - desc_node(p, *at_start).ok(); + desc_item(p, *at_start).ok(); } NodeKind::Colon => { let marker = p.marker(); @@ -261,7 +260,6 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { | NodeKind::Let | NodeKind::Set | NodeKind::Show - | NodeKind::Wrap | NodeKind::If | NodeKind::While | NodeKind::For @@ -282,7 +280,6 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { *at_start = false; } -/// Parse strong content. fn strong(p: &mut Parser) { p.perform(NodeKind::Strong, |p| { p.start_group(Group::Strong); @@ -291,7 +288,6 @@ fn strong(p: &mut Parser) { }) } -/// Parse emphasized content. fn emph(p: &mut Parser) { p.perform(NodeKind::Emph, |p| { p.start_group(Group::Emph); @@ -300,7 +296,6 @@ fn emph(p: &mut Parser) { }) } -/// Parse a heading. fn heading(p: &mut Parser, at_start: bool) { let marker = p.marker(); let current_start = p.current_start(); @@ -317,8 +312,7 @@ fn heading(p: &mut Parser, at_start: bool) { } } -/// Parse a single list item. -fn list_node(p: &mut Parser, at_start: bool) { +fn list_item(p: &mut Parser, at_start: bool) { let marker = p.marker(); let text: EcoString = p.peek_src().into(); p.assert(NodeKind::Minus); @@ -332,8 +326,7 @@ fn list_node(p: &mut Parser, at_start: bool) { } } -/// Parse a single enum item. -fn enum_node(p: &mut Parser, at_start: bool) { +fn enum_item(p: &mut Parser, at_start: bool) { let marker = p.marker(); let text: EcoString = p.peek_src().into(); p.eat(); @@ -347,8 +340,7 @@ fn enum_node(p: &mut Parser, at_start: bool) { } } -/// Parse a single description list item. -fn desc_node(p: &mut Parser, at_start: bool) -> ParseResult { +fn desc_item(p: &mut Parser, at_start: bool) -> ParseResult { let marker = p.marker(); let text: EcoString = p.peek_src().into(); p.eat(); @@ -366,7 +358,6 @@ fn desc_node(p: &mut Parser, at_start: bool) -> ParseResult { Ok(()) } -/// Parse an expression within a markup mode. fn markup_expr(p: &mut Parser) { // Does the expression need termination or can content follow directly? let stmt = matches!( @@ -375,7 +366,6 @@ fn markup_expr(p: &mut Parser) { NodeKind::Let | NodeKind::Set | NodeKind::Show - | NodeKind::Wrap | NodeKind::Import | NodeKind::Include ) @@ -389,7 +379,6 @@ fn markup_expr(p: &mut Parser) { p.end_group(); } -/// Parse math. fn math(p: &mut Parser) { p.perform(NodeKind::Math, |p| { p.start_group(Group::Math); @@ -400,12 +389,10 @@ fn math(p: &mut Parser) { }); } -/// Parse a math node. fn math_node(p: &mut Parser) { math_node_prec(p, 0, None) } -/// Parse a math node with operators having at least the minimum precedence. fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option) { let marker = p.marker(); math_primary(p); @@ -457,19 +444,18 @@ fn math_primary(p: &mut Parser) { | NodeKind::Ident(_) => p.eat(), // Groups. - NodeKind::LeftParen => group(p, Group::Paren, '(', ')'), - NodeKind::LeftBracket => group(p, Group::Bracket, '[', ']'), - NodeKind::LeftBrace => group(p, Group::Brace, '{', '}'), + NodeKind::LeftParen => math_group(p, Group::Paren, '(', ')'), + NodeKind::LeftBracket => math_group(p, Group::Bracket, '[', ']'), + NodeKind::LeftBrace => math_group(p, Group::Brace, '{', '}'), // Alignment indactor. - NodeKind::Amp => align(p), + NodeKind::Amp => math_align(p), _ => p.unexpected(), } } -/// Parse grouped math. -fn group(p: &mut Parser, group: Group, l: char, r: char) { +fn math_group(p: &mut Parser, group: Group, l: char, r: char) { p.perform(NodeKind::Math, |p| { let marker = p.marker(); p.start_group(group); @@ -483,15 +469,13 @@ fn group(p: &mut Parser, group: Group, l: char, r: char) { }) } -/// Parse an alignment indicator. -fn align(p: &mut Parser) { +fn math_align(p: &mut Parser) { p.perform(NodeKind::Align, |p| { p.assert(NodeKind::Amp); while p.eat_if(NodeKind::Amp) {} }) } -/// Parse an expression. fn expr(p: &mut Parser) -> ParseResult { expr_prec(p, false, 0) } @@ -571,7 +555,6 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { Ok(()) } -/// Parse a primary expression. fn primary(p: &mut Parser, atomic: bool) -> ParseResult { if literal(p) { return Ok(()); @@ -599,18 +582,17 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { Some(NodeKind::LeftBracket) => Ok(content_block(p)), // Keywords. - Some(NodeKind::Let) => let_expr(p), - Some(NodeKind::Set) => set_expr(p), - Some(NodeKind::Show) => show_expr(p), - Some(NodeKind::Wrap) => wrap_expr(p), - Some(NodeKind::If) => if_expr(p), - Some(NodeKind::While) => while_expr(p), - Some(NodeKind::For) => for_expr(p), - Some(NodeKind::Import) => import_expr(p), - Some(NodeKind::Include) => include_expr(p), - Some(NodeKind::Break) => break_expr(p), - Some(NodeKind::Continue) => continue_expr(p), - Some(NodeKind::Return) => return_expr(p), + Some(NodeKind::Let) => let_binding(p), + Some(NodeKind::Set) => set_rule(p), + Some(NodeKind::Show) => show_rule(p), + Some(NodeKind::If) => conditional(p), + Some(NodeKind::While) => while_loop(p), + Some(NodeKind::For) => for_loop(p), + Some(NodeKind::Import) => module_import(p), + Some(NodeKind::Include) => module_include(p), + Some(NodeKind::Break) => break_stmt(p), + Some(NodeKind::Continue) => continue_stmt(p), + Some(NodeKind::Return) => return_stmt(p), Some(NodeKind::Error(_, _)) => { p.eat(); @@ -625,10 +607,8 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { } } -/// Parse a literal. fn literal(p: &mut Parser) -> bool { match p.peek() { - // Basic values. Some( NodeKind::None | NodeKind::Auto @@ -645,7 +625,6 @@ fn literal(p: &mut Parser) -> bool { } } -/// Parse an identifier. fn ident(p: &mut Parser) -> ParseResult { match p.peek() { Some(NodeKind::Ident(_)) => { @@ -762,8 +741,6 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) { (kind, items) } -/// Parse an expression or a named pair, returning whether it's a spread or a -/// named pair. fn item(p: &mut Parser, keyed: bool) -> ParseResult { let marker = p.marker(); if p.eat_if(NodeKind::Dots) { @@ -806,8 +783,6 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult { } } -/// Convert a collection into an array, producing errors for anything other than -/// expressions. fn array(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { NodeKind::Named | NodeKind::Keyed => Err("expected expression"), @@ -816,8 +791,6 @@ fn array(p: &mut Parser, marker: Marker) { marker.end(p, NodeKind::Array); } -/// Convert a collection into a dictionary, producing errors for anything other -/// than named and keyed pairs. fn dict(p: &mut Parser, marker: Marker) { let mut used = HashSet::new(); marker.filter_children(p, |x| match x.kind() { @@ -838,8 +811,6 @@ fn dict(p: &mut Parser, marker: Marker) { marker.end(p, NodeKind::Dict); } -/// Convert a collection into a list of parameters, producing errors for -/// anything other than identifiers, spread operations and named pairs. fn params(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { kind if kind.is_paren() => Ok(()), @@ -866,7 +837,6 @@ fn code_block(p: &mut Parser) { }); } -/// Parse expressions. fn code(p: &mut Parser) { while !p.eof() { p.start_group(Group::Expr); @@ -880,7 +850,6 @@ fn code(p: &mut Parser) { } } -/// Parse a content block: `[...]`. fn content_block(p: &mut Parser) { p.perform(NodeKind::ContentBlock, |p| { p.start_group(Group::Bracket); @@ -889,7 +858,6 @@ fn content_block(p: &mut Parser) { }); } -/// Parse the arguments to a function call. fn args(p: &mut Parser) -> ParseResult { match p.peek_direct() { Some(NodeKind::LeftParen) => {} @@ -931,8 +899,7 @@ fn args(p: &mut Parser) -> ParseResult { Ok(()) } -/// Parse a let expression. -fn let_expr(p: &mut Parser) -> ParseResult { +fn let_binding(p: &mut Parser) -> ParseResult { p.perform(NodeKind::LetBinding, |p| { p.assert(NodeKind::Let); @@ -965,45 +932,30 @@ fn let_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse a set expression. -fn set_expr(p: &mut Parser) -> ParseResult { +fn set_rule(p: &mut Parser) -> ParseResult { p.perform(NodeKind::SetRule, |p| { p.assert(NodeKind::Set); ident(p)?; - args(p) - }) -} - -/// Parse a show expression. -fn show_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ShowRule, |p| { - p.assert(NodeKind::Show); - let marker = p.marker(); - expr(p)?; - if p.eat_if(NodeKind::Colon) { - marker.filter_children(p, |child| match child.kind() { - NodeKind::Ident(_) | NodeKind::Colon => Ok(()), - _ => Err("expected identifier"), - }); + args(p)?; + if p.eat_if(NodeKind::If) { expr(p)?; } - p.expect(NodeKind::As)?; - expr(p) + Ok(()) }) } -/// Parse a wrap expression. -fn wrap_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::WrapRule, |p| { - p.assert(NodeKind::Wrap); - ident(p)?; - p.expect(NodeKind::In)?; - expr(p) +fn show_rule(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::ShowRule, |p| { + p.assert(NodeKind::Show); + expr(p)?; + if p.eat_if(NodeKind::Colon) { + expr(p)?; + } + Ok(()) }) } -/// Parse an if-else expresion. -fn if_expr(p: &mut Parser) -> ParseResult { +fn conditional(p: &mut Parser) -> ParseResult { p.perform(NodeKind::Conditional, |p| { p.assert(NodeKind::If); @@ -1012,7 +964,7 @@ fn if_expr(p: &mut Parser) -> ParseResult { if p.eat_if(NodeKind::Else) { if p.at(NodeKind::If) { - if_expr(p)?; + conditional(p)?; } else { body(p)?; } @@ -1022,8 +974,7 @@ fn if_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse a while expresion. -fn while_expr(p: &mut Parser) -> ParseResult { +fn while_loop(p: &mut Parser) -> ParseResult { p.perform(NodeKind::WhileLoop, |p| { p.assert(NodeKind::While); expr(p)?; @@ -1031,8 +982,7 @@ fn while_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse a for-in expression. -fn for_expr(p: &mut Parser) -> ParseResult { +fn for_loop(p: &mut Parser) -> ParseResult { p.perform(NodeKind::ForLoop, |p| { p.assert(NodeKind::For); for_pattern(p)?; @@ -1042,7 +992,6 @@ fn for_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse a for loop pattern. fn for_pattern(p: &mut Parser) -> ParseResult { p.perform(NodeKind::ForPattern, |p| { ident(p)?; @@ -1053,8 +1002,7 @@ fn for_pattern(p: &mut Parser) -> ParseResult { }) } -/// Parse an import expression. -fn import_expr(p: &mut Parser) -> ParseResult { +fn module_import(p: &mut Parser) -> ParseResult { p.perform(NodeKind::ModuleImport, |p| { p.assert(NodeKind::Import); @@ -1081,33 +1029,29 @@ fn import_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse an include expression. -fn include_expr(p: &mut Parser) -> ParseResult { +fn module_include(p: &mut Parser) -> ParseResult { p.perform(NodeKind::ModuleInclude, |p| { p.assert(NodeKind::Include); expr(p) }) } -/// Parse a break expression. -fn break_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::BreakStmt, |p| { +fn break_stmt(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::LoopBreak, |p| { p.assert(NodeKind::Break); Ok(()) }) } -/// Parse a continue expression. -fn continue_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ContinueStmt, |p| { +fn continue_stmt(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::LoopContinue, |p| { p.assert(NodeKind::Continue); Ok(()) }) } -/// Parse a return expression. -fn return_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ReturnStmt, |p| { +fn return_stmt(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::FuncReturn, |p| { p.assert(NodeKind::Return); if !p.at(NodeKind::Comma) && !p.eof() { expr(p)?; @@ -1116,7 +1060,6 @@ fn return_expr(p: &mut Parser) -> ParseResult { }) } -/// Parse a control flow body. fn body(p: &mut Parser) -> ParseResult { match p.peek() { Some(NodeKind::LeftBracket) => Ok(content_block(p)), diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index f18bb7805..b27832c53 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -613,12 +613,10 @@ fn keyword(ident: &str) -> Option { "let" => NodeKind::Let, "set" => NodeKind::Set, "show" => NodeKind::Show, - "wrap" => NodeKind::Wrap, "if" => NodeKind::If, "else" => NodeKind::Else, "for" => NodeKind::For, "in" => NodeKind::In, - "as" => NodeKind::As, "while" => NodeKind::While, "break" => NodeKind::Break, "continue" => NodeKind::Continue, diff --git a/tests/ref/style/set.png b/tests/ref/style/set.png index 502d74b5d89fcf50a355e12f507d5c29d1b8892e..6e154980045dc39701345f7735e1b12151f8d0db 100644 GIT binary patch delta 4919 zcmZvgcQ~7E8^(EQ8yZThH1<{_v{BS9YStb#V}_zAF>9q(tXiRB)~;EqLDlSQh1jFi zuDxnh?D;+K|KI)Rb3Dg!U-x-k=kL6qXELAkOB-n-19c+k9}*IhBk$x7BqY|y)jU>O zDB<6_Mb~8VgL#k(l_D)dohC!khN_dYagsTw0FqzPuW)MrxV;0jEBV?#C=(WWY|+*| z#_nIRRmt|g;H5Ks%9*v^(IurlYLkp;YdlD4>e_WoTVdJI;nAbWC1@vzzk%yRIhfa< zX^WW--Gj}1_l!JyhknK$_5OWdf4{w(jQj6rOiY=_%pe2_q+vj&$RD3Oqaz?K%^{`%wE1;mpI#x;Vt;Up!tUa zLPtke!rEqT+7kG^un?iAM+?_SSy;%}b%7lr&(J#TVTb5yl}fv{VEOS5@5MbQq}2F5 z=%4#D@N;qXA=*fdd@wGd{aDGrfF7^@BG*%W7g0f=K)CetJ?1T zX!|t+ffxxoL-tHGN$Ztx2T9BrW{s=j=o`R{y! zVbk$F@^0yE^JS}FO#4Taf_ix2TZabT#(}O)8gn!7;lSok&8hO>nv~DUXkAMg2&p-J z45>RG>7Ur0n9itRI&U<3_NT&oK{Swy3LeTA$!$*FHF)lk+4B3#OWpj1RFi3)E1{=S zQq-(6X&G<_816>Y-NZbGpj%V(^Euq=aydDfndi$r$%~7Ng2t78Z{J2oMTMi$iYf2F z&1&ebE)}^PArUB43-b#+ZlevOSC zRJw9#D1(!{L{rf$5{8qi<#A zB5pIVFJ9k(F-v)=DJdx#8m71?SXfw?nwqlH7#bSZOe2Y_{p1QV{s$$!cmW|HZV>0A zM~T9EMn>7FryjPx1M>ue^RF*9Zfk9jl zl9HkEaR=Pw&dv@#o<&eVKtNCsXxBcGn2fk7aVm2h*MLmU=6@plO?J#JxX$;!mE zwXp$VW)9pKDH879+goUF4mfUTY}}n|mX?>lhGX4N_gB)=9ymE|cr0~wc64;$@dl_B z4-XFp5zFVG`*d{Nyu5OOCy8?_0BuM~NdYYYxK&v;+m8EOo*ky8r2$8{%^>XO?+-Y1 zm_jEw_^K<8=`=9d#l^+blW4B9dbB;Al$2y4?7Kdc_ZT`hHulKgZ}0cNpR_Vh0)sU* zHSzJLa5x-^{F;z3xZ0mNn1zxggIxr{ngUPtH8ccdWU|cegUE&GW7(rZ$ZmCYcG8l2 ztPt*p>uP9F#&(ufR3HfM%iT#WLFdTt7G;?W%gY@VlG4)VmX?-^ii$=?A9`0X+#9Q_ zDz|iBA)?~rQ&Un9xJgP%%0*~+L`3tUz|71{+F*1_3Z2owf;&()Dl{xi`T27-Od)q^ zWo2D;H3!J{$2(FBVdLk|5eMtT5^giOugx2Mpz0WD-yJ#z1~ze*2eF-;>M(Y8fEP)sK-T>0@l;h`}+0k{{FtLtu48Nqq8$C=%J8M)+3vL zVmlcn+_LiX^P{3rQR(TX(#8ttj~EVq{^(BIpFe+cgx|e;7Zkd8c-Y_D+vxW<|I;U4 zsFJes4Rs7K_%&c|bz!rb^-0_-P#$1HS65dIcj-5q@S$?!Dl`}Z3pzW%VD6T23kkhv zkCF{I^3q2GSdET}nVOu`%hNSCZ$z#D^@@s$N?R(U0Ka&9*ETgZef((Y?Jahwt=W@X zQsQW9i#*v|m=8YRJ|(V!@_v>Z$3{mFe^ymc2#JXybSH6bZEdB)eoapsaNPnhs-d9) za70B##c*rv*DuF6Z*(xr)pmHPkD7LNcHZ8HHBO^l?;!@W|A=a3Jhtls2D3yEi9~XO zP@$5ApDHUWb93F-23VZM#Kb=IJ5-s~QX)>I5$?Xe#`^mCsl_@umHGLK@^ox$I=$~` z7$4hq)VWTlLCG#@6xTj_kqkjBO4VsH$UJCQ41}T+-9hOiWE7-9t+- z6LWKO6BFIuk&%(r)zt$fNlD4RD8Rb2^&U%&QbQtEE!?2RNVeeImuvl*5-8+yw-0`i zc*?Eb0(4pw24;NHJvLUeL_-zZ>Fnb2rwJ@BF5ZW!sd*C0zRnx3qOXsc!(c+3RUX1% zFf|OK>#<@~zk}$xUUo_zyaGk+m+<%ZAN%_4+c%(#uC7qeu%Pp!B<{CY7jF~u@b)HO ztV3#R#cG8(IBcApCbd!MUaW{gIp7d4!kiuuj5kBs0d$ra@$eM+X7A;ou1*yogx+3t zjF*p(n{OR3Nj#uMxa`J2_ZK@nbtwBlw)BLDRRh(=!}jlV4tvTwoOxC_ytb zGn04OrKA`U>mHC)VYmh#D=;YsjdY6Z3G!FD_9dmL(xk*hH7%{}La;%xwvnM>)4q6# zN7-?&u9ek15X-8nc%N4Q*VJjqXYXu;f^ShXvx64@OyahWSfsoj25n!bfT^je0gGWp ztaeTc_4@g{hj1_;$N&^!&5ri>?=mp7dT-I!ZZ0fvaB?cz)6vq>0@6`YQDvw6JAfL9 zXC2v?k|7h-aIC1Pm@{yFh3Fm_;04(N5wNWys;sPRTw(HeknnqVR|kXem0SJ?<`vu2 zBqPQ1xuSwuKDepAzP_R13~+>!qN1jjmc70G;o+eqvo;dBAHX|1)#JW7#vjTKq;U72 zD>E~*5xo-yNMK+ffZv_D<`*ceOz=g$Rcml>Ds(Fz3{+L15;rn5WKEBbj1(pAqcqUK zX7sTuD=VEdKib>vW*fZO+vlHbI3N&(6&0GzTNC5sD$2^ICnvzIO1B^;Q{0sj3~qW$ zBW%Azh0C&F&sVPs^)aDVsRwQMl{VOtIG*a!9Jm%@h!jEsbyo`~>p zI(mBjFdP8CmoLSiJbA*!#l^{K3vd9?tEi}GHi?wO>*B-%02>@W8y+5BQ&R(g0XONg z>X7sN=Iz_D|AyU)3=jV^Ra>468?yVaF?`xtYMPogI2@NcW^gSGj7kRIxN!qW6HhO% zLe&m=l$y0Qdt^Is-Ls{x^nA*^nrN@26xP@n0LWiTWCom`rxZCAw@Llr(L3#ckjZ$+CUr_?O&XF0yYQoE0q10OI=6^DUpkV1tRLu2gFx858yu*X;x5PU!Nvxf+~hv zfyOH6@IzIVh^N%##00i|;nCnV7K&4 zenCMdMn>2RAYK50OM-F0)eZNT9C8(ZXVc!n0jSG*tVB;Wp7|~6hj&O@TN}VZ;oZ5R zp+qbepXGjHn1aQA%cM9Bd?+B$fV#eFyqN^R{t|RtpqlXK&mX=kV$L-UIR_NlZ^upp zq9Hk8g*gADOisa6h03qZQ!qBk3(pz7%?BS2$^I`HDH(!sA&(bRL zB`gxc#9QI$14E1JBWRl{KcoePQ|N@w*SNWLLEon`PO! zZUFyf(i|L7W#~)!GQ}aT(fxd>r~XQ8r(W#!DX*X#xOPl;+%#%h(lQce0qu*C>&bt9}Avb^LT$%lh@zn)r@d5rPTE)3EcR-y0mbJl zN8MNbve%f!zc-I>PrIOWkMenBV;$je=h%R2I?{iZ54p}RW#p(-rbg8HyIt5GJFZvt}&x$JKbMAsDk+%2#<{KT&FYUPG+=OT+6~9 ziI%e{_=PbHPP+{Kin_fa7z1uUG!S~!0pFW4E0kwpksg_{N&Ae7o|}PeZ^QbA-H2Hv zR$sy9mqYvGjnB9uBV0d)znP~a=o*CCt?M80r7FCj+MbEQ(JDck&h%irhoJXUp`~e-nu?%4w@;k{iM%tx4ew(xTG@Usabwht5J1# z;B%M5$H~LclpT{95B=F#izI2HJrmprd+Qtn9Tv>yuWrgcBvbrM+T6vGt=?V?-TSw^ z`quQHy9}r}bCPDK^}*|V^uppw^q%gL_Yz4FKOphZ)bWKAY{Y3Bi8sF!^W}bi+YGp% zm)5a*dURG%*e{?w5@ZMq3(58;}>l50m$HgwBzk9JE`PH;*e62Zn z{->8%-&>a4xyL=pHk1!ZVad64bi)7VlNopvXnVzGZoEYQu~L}hzcNU{^l|Intj;W&?9G-e z@rBWx#)mhM`S8LQzF1WjtS=iMEUOD_L!?g-pnF8f=L#plp|-cj-0 zVAIbxYkz+oFa5Fit%vRWM?Dq2Z8oFPsyBBkwJpvjlde>vki4xFWgSNho12??d3j7G zQzQ~)WMnWH442ClJ|m*hXf~T|?l0!&=Me*9c-{fRz>2i0sTC5g>ra{|att)@4E!R5-3M=c5BZj4=rNcjHxZUoF zi3y6LBHx^plw>p-|6xOj0Wth{Z_2WgvQJ6N6jw;sd#w(Wws`caF)-9WaM|X)CQ}*C zT(A==TW+~olA}n?EM}jQr@o;|=T&A2>cqzp1DDG^{D$zi+S=M!ELJcW46g}=LM0_7 zI5K}Ah9iifv7gX5iFewGN~bupC{58Q);DuI-52eiYN?5rQ%@9_vd&g;vUU8Tmb@af zFy9~)>2r&k3I(;<#}NZT5OHyF8jZ&IlHG21yWJ}*D~GRcZ*M0jCv!L)zu!MQJL~m& zc|2ZxeEizl8e%{UuNy<>?ShVBDLEo(`AC0K-*wJ%L)q9@sJz6{zMrnU%G349nmwh4 zev!IeD5^P~-^ed@6w5D3i|si=1OL^x5)1|-A3hREAP}h4>hR+9^fZgb;{G8mEseoo zL`O$YO-+Rl1uB&)CMJePqtWSfxm>=mun_+E*w|QXZ0z##GDZq9{A;66w6~wufA)XG zbmLBi(<5^ZDV)P%$Dq9HQyDp`xc-&O=|9{1KzMCZ()zKq`NQ*;L9uZlzoB1T)g?Ug zy)ZK~lbD#ezP=v0G876WBqaRlk+pk!dlW_O?CfB45W|thF#b)A@9Xj}UR2+Cp}7Bj z^_Mf|yFcjq?p+vpt_=Lt@JXQT`gniIz=Yayv*6Zq{m|p8j!~8Sk=*-CJsdC}`ChPE zttlxfhrcdII{y-;1TmbTYoI+I51mdYNs^+djg5^^C^R=W*VEGz6&2<4`7kAj;RMC7 zzrRnCB%95qN8t1MC;>5?(7R(C92{(IZ7nV?uCA``?(SkZ5Cd)%1Bd}J0AgG~3;>7$ z05Jd{1^~nWfEWM}0{~(GKnwtg0RS-oAO--$0Du?(5CZ^W06+`?hyegG0Q|pW00000 m00000000000001ZMZW`TIBGn^#fRzu0000!6PiKkC5V93 zpb(lMpeRk0-g^xQggc&d&v~vs=X|*NlG!tRcJ|C(d*!$O2~D(z9kfq{j=7Lui*T3% zn`QY6Ettt!?Uy1>zIU?SahTmow%6&aawjZYZuPPIAzM6gPA+Vm*~tMq@Wyn4g^N`GM#!?xw>U# zom-#Y?)jU6L2&t)ivi5IysqDQc@B*Jk^2aQw>{F zg?;Jox3RMF_v0l+9R8e}o14W52naaM^%AD6=D;zCq@RVY;a#+dT@iH1V*k*$Zz@Vk z9GskqqsMbCigi}&G&Ma4$84S9Qn0eJx+PYe-0lq2Uv4LT>~Bt&EM0{qn_p#pb?2 z&V-|2*zTIZUQV(>PEsA4R=0Y9Wi%HTSB+z&zPgd2;pC5Q+(Ef0_3^oL=MF{%tQ{P> z8!FWtZ{9q2L{YhEZf;i_9CH*y1FHz3IkQHIcWQcgY8lvcj(ny}PeZ1OV$;<14N0iHcB zIc7s8wu;nC^Zg~?xcuswd3e6>pj};Ekx1l7)v^1xy3>_&a&j(1)6&v{FfvRh(GX7A z@SUaI-7u;1fzn%9S&@V5>*_kq^P`PpoZdy!mX?-!Rn|H-M)c*G05kVY zOiWOyLr{yGCuAH+MtxhumEIx9iLtR44J(EQ1`o9fFJC%?do3Js%i2Qv8RL05kHIo0 zi4=yS6R==^NI^k?9Q5VsIaiGvM2izeX1)8h4RFAfB4X}patRKj>^l+D=6?ZG(6o|&dJFs z2M2d&+44(w!u7fF@%Qoh=7rCnb2vLsiWsKOjgXt0qsU{6i{Gfwej5m+8Mw|GGS$vp z%uDV~go&jXoId@e#@$qqUQ&<|P4Ow*a z^BWzu@FVTbv?mJ5)8CXxxqtsY7JI|H6QAoTBqT(AVN;S2AMXoHzPj!W+uhy0&YFV7 z{&OgFd;3*Qe|l3_SJxV5VPWC@?6g%Xk4mW2-njq#XAz`- zGy04ioRM4UqJl!zAxutQ-rCj{!vWJSv2fnzNB{lAslV7-PfrgHho7Gm7Z;a<@9*z> z`w3z@Jn#VZ=OR(Qb+!v9#LDm_Ha0jS``tTDmZ(RM9ubM4yNCa}!gSIDub`kHEiH|7 zhnzWc20UQZ&A1%GUFE1~fn!ucxm{T2^Vyx0u zWn-=JT)sXCU*C@?HgF#^3+YwY)QCW#&Y$?v&!$91L_|bdT3XHz5NOx3!fBX*KMnC; zM)aFY{`+K@IO2E1qnYEVj${l|rvGpD`O8iJ@VNgwG(e_V3v9V=l_KNhlXa)FHk|Xw zJd%UD0lOFQsdJLN)tM7xRQe1|AY{7?3%sxbG#N2Kv*Maab zMd5EA&2}9t-#8{6ao&;@7oxa574N_LhA%0nN)d$(g@6;2?Y=YXs`0sNpV_aDCkqJP zMl#^8+u6#g9q_TV?i@P%Wr;x~i@PKXNfAHY$Ro~^uj0b=s!_U=YE(Mj>RNcyAXPH> zivJ49`<(JyFXXF~Op1w(xGR~ZRYA<15f@r8<#TH$6*cepRHk<}T^~Gt*L4AQoB%=& zfesNZeNy1`PXp_uR{O*O%u`;Vd^N}O$R%ubZtnP}8`7!iriHe03U;A)!3C62mVaaH z(9B1~4oF33O1YA4rSELE&GK?VoqRC%D#eE*_s*(&Aw8!UCCnK%j!!(Ng;gk`b)mT^ zmes<|RExWrMivD(r4!G-irYOR-Yebwq^>q8>g3bV6Oqr;ocwe=C7uFQDt0tv886hD zrVvr9)1S#u3MsH?hMuz^MMK!6`h{74rVJCPsZnknuD5-PLJ>GSoW9-$pUeEc3a}|0 zw@e;$I#^^8M!V*@FOIkXt#0B%p_l+4UvU!%uc#||#FS`I5(o*mZyMy4IjIMUCfq;_ zW=`xkn~W~hf4>WaMSWuw!15_rF5Iga*`FV_EUuyR$Ve-W4oQ`rfOizxgriGMS;U2Y zWWC05S*Ps+EUlql`QP6csW3=xR#56daGBe3MmH;mx4SIg&>JCK#69(2bWw5kS5dz< ztIQXl<~YHE#NubX2EczLXm<5C{KFQdNIi&*AbRbZ_Sior%VD-EoYXO5`VjqU zLJ5#1RIFI2I1eTt#DFV~IUL(-Oug`|ULDCp4Z&` z5S<}3I?hQ3iO}xr}*aY2PfkdD-19*-_Y%j!$wLw1Z$IM5+_nu*F$p zK9cXzTU}I7a`DQ~c=%ck50o9k0J%!Ju9I({$xiSTcn7cChM(eGNn1$pxHQW6qUFIKI<5^5-Y9g3ch3=-|Q^&Pku zcsFIJiZkx5@5+~An*7V_8pj{Ze%H?#5loPbO=qT}lbQFSCB5oMYZTHHutvc&(kPgQ z3r;?_sh1^EY~;mvZzj!U)Y{%&Kz{wehZqt-{~dkb&WPXJ{6lmedbmdKn&|OH1NC%v zc|twuiZk_+^DXP0#WFZg( z{C{BmxBszshUaups0Jg^&Ym7%Yc8?XUedCZ8>A9y#@IAQ019=VAjinTu_u;>D;Gy< zf%4zj+!{^Q3FE=6QldwXvt zh)7_Pn_6Dx;otz%SbMxi*c!i-RB?TMx`wisW4Gs>z zdv|MX>^&Iez=MHO`>E1}1+V*>Wg&|e$Ur1gTG!O9Ao=Lz;m>W`Dj&|h&8Gr7ugvE2 zvLd`dn9Vcc17fqU2!5&wnk}`t>|WWg#K*xa(F{`5>b$E!kOGwv`ua6V}$(31Sio3aLhAZ{KEJ4w0Zlf;`*b z*LUgSMgC3jb_t1WF$oEY*eCD5eA#+;pH3nr7H}P^sE`AJ^tDx7A= zp2I6dhXyt&tEb7yub86<1cEkUOz&@QB~4Rv1VV6cEs9Zn4ouBAZkz>*z_5+sR!%w? zYHQ02r>ub4^`27b(jZ3tXTISxX=Y|-mnjh1@7}#DFE4Kg8L1+dyLO7j78Vzi0|78T zMPvvlDpH=vke(|AWRpnh@ID6zhv7Cz`m0yMvDpuzqBxp`IMU9G`VgrnoA0zxTx?&*%Tj3y+58VJD|>Gf4KHUi;@>qTEd zO$-fLPiiS3N3NQF0j1UP7%K|L!<^|m%TKdQ2L}cUvhM?8u?Aq`l$M@Z4Hlp$ueFh1 zTo?25_LiqVefqQ~{_m3~Jxo9YPvC$xy??JYsl|4|#s#b=kk;Qw)uHxxGS$LC@sd#c z5t|I#Vy$ z*9MORS#e;l6KtCpxf&X(plb?g?v=B*u`!@mQ&nw^<9ri5|M?nLbe)e6WB`pZXfwDQ zJ@KNWV8VPE6%}P~XZP1J+MygzcSvB(NPmBSQxl>mQ!OSYW)wyF4z4*EIcmHIAL4Mh z`1p8GRf>TJy8&uyY9R6Zeyb9o-vuTyoD-9iJlx!Kc}8%!#?e7gAX~J5K)~y&su{l| z98R1iMq69EIuVaQ@9qv}%B!ZyBofJ7V%lMtjMD%}SA}irLN6$qfmI?mZ{F-( zT3%Y>=Hb~B($dlrOg!n1Womw+6j@qQ(f}5Uz+{;@tCeivYHG?1th)1!5m5@Kesi`z tN+h6#t__^{5B~a(62~8L;Bw~?&E>t$<;zm+4}V>{rfYnqRQuM0{{bKbY!3hc delta 4096 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 g7HppMUoV@y)$w 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 text("IBM Plex Sans", eval(it.text)) Interacting ``` @@ -31,19 +31,18 @@ Blue #move(dy: -0.15em)[🌊] --- // Error: 23-30 cannot access file system from here -#show it: raw as eval(it.text) +#show raw: it => eval(it.text) ``` -#show strong as image("/res/tiger.jpg") -*No absolute tiger!* +#image("/res/tiger.jpg") ``` --- // Error: 23-30 cannot access file system from here -#show it: raw as eval(it.text) +#show raw: it => eval(it.text) ``` -#show emph as image("../../res/giraffe.jpg") +#show emph: _ => image("../../res/giraffe.jpg") _No relative giraffe!_ ``` diff --git a/tests/typ/code/break-continue.typ b/tests/typ/code/break-continue.typ index 2415cb8f9..fb3222fa2 100644 --- a/tests/typ/code/break-continue.typ +++ b/tests/typ/code/break-continue.typ @@ -116,7 +116,7 @@ // Everything should be in smallcaps. #for color in (red, blue, green, yellow) [ #set text("Roboto") - #wrap body in text(fill: color, body) + #show it => text(fill: color, it) #smallcaps(if color != green [ Some ] else [ diff --git a/tests/typ/code/field.typ b/tests/typ/code/field.typ index b63a8768f..abea87fb3 100644 --- a/tests/typ/code/field.typ +++ b/tests/typ/code/field.typ @@ -14,7 +14,7 @@ --- // Test field on node. -#show node: list as { +#show list: node => { test(node.items.len(), 3) } @@ -32,7 +32,7 @@ --- // Error: 29-32 unknown field "fun" -#show node: heading as node.fun +#show heading: node => node.fun = A --- diff --git a/tests/typ/code/import.typ b/tests/typ/code/import.typ index b554d6e7a..5291af395 100644 --- a/tests/typ/code/import.typ +++ b/tests/typ/code/import.typ @@ -1,4 +1,4 @@ -// Test import statements. +// Test module imports. --- // Test importing semantics. diff --git a/tests/typ/code/include.typ b/tests/typ/code/include.typ index cd3328a23..e862adac7 100644 --- a/tests/typ/code/include.typ +++ b/tests/typ/code/include.typ @@ -1,4 +1,4 @@ -// Test include statements. +// Test module includes. --- #set page(width: 200pt) diff --git a/tests/typ/graphics/shape-rect.typ b/tests/typ/graphics/shape-rect.typ index 7d1101808..94686da27 100644 --- a/tests/typ/graphics/shape-rect.typ +++ b/tests/typ/graphics/shape-rect.typ @@ -47,10 +47,10 @@ --- // Outset padding. #set raw(lang: "rust") -#show node: raw as [ +#show raw: it => [ #set text(8pt) #h(5.6pt, weak: true) - #rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), node) + #rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), it) #h(5.6pt, weak: true) ] diff --git a/tests/typ/structure/desc.typ b/tests/typ/structure/desc.typ index e12bbd161..af1b29865 100644 --- a/tests/typ/structure/desc.typ +++ b/tests/typ/structure/desc.typ @@ -37,7 +37,7 @@ No: list \ --- // Test grid like show rule. -#show it: desc as table( +#show desc: it => table( columns: 2, padding: 3pt, ..it.items.map(item => (emph(item.term), item.body)).flatten(), diff --git a/tests/typ/structure/heading.typ b/tests/typ/structure/heading.typ index d37d8796a..d08b3687a 100644 --- a/tests/typ/structure/heading.typ +++ b/tests/typ/structure/heading.typ @@ -1,7 +1,7 @@ // Test headings. --- -#show node: heading as text(blue, node.body) +#show heading: it => text(blue, it.body) = No heading @@ -46,8 +46,8 @@ multiline. = Heading #set heading(family: "Roboto", fill: eastern) -#show it: heading as it.body -#show it: strong as it.body + [!] +#show heading: it => it.body +#show strong: it => it.body + [!] ===== Heading 🌍 #heading(level: 5)[Heading] diff --git a/tests/typ/style/set.typ b/tests/typ/style/set.typ index 25dc1b814..2864b81bb 100644 --- a/tests/typ/style/set.typ +++ b/tests/typ/style/set.typ @@ -48,6 +48,19 @@ Hello *{x}* + Rhino + Tiger +--- +// Test conditional set. +#show ref: it => { + set text(red) if it.target == "unknown" + it +} + +@hello from the @unknown + +--- +// Error: 19-24 expected boolean, found integer +#set text(red) if 1 + 2 + --- // Error: 11-25 set is only allowed directly in code and content blocks { let x = set text(blue) } diff --git a/tests/typ/style/wrap.typ b/tests/typ/style/show-bare.typ similarity index 55% rename from tests/typ/style/wrap.typ rename to tests/typ/style/show-bare.typ index e37c4bc9f..2dba742f0 100644 --- a/tests/typ/style/wrap.typ +++ b/tests/typ/style/show-bare.typ @@ -1,4 +1,4 @@ -// Test wrap. +// Test bare show without pattern. --- #set page(height: 130pt) @@ -9,28 +9,25 @@ T. Ypst ] -#wrap body in columns(2, body) +#show columns.with(2) Great typography is at the essence of great storytelling. It is the medium that transports meaning from parchment to reader, the wave that sparks a flame in booklovers and the great fulfiller of human need. --- -// Test wrap in content block. -A [_B #wrap c in [*#c*]; C_] D +// Test bare show in content block. +A [_B #show c => [*#c*]; C_] D --- -// Test wrap style precedence. +// Test style precedence. #set text(fill: eastern, size: 1.5em) -#wrap body in text(fill: forest, body) +#show text.with(fill: forest) Forest --- -// Ok, whatever. -{ - wrap body in 2 * body - 2 -} +#show [Shown] +Ignored --- -// Error: 4-18 wrap is only allowed directly in code and content blocks -{ (wrap body in 2) * body } +// Error: 4-18 show is only allowed directly in code and content blocks +{ (show body => 2) * body } diff --git a/tests/typ/style/show-node.typ b/tests/typ/style/show-node.typ index b35ab4c49..56b7e34aa 100644 --- a/tests/typ/style/show-node.typ +++ b/tests/typ/style/show-node.typ @@ -3,7 +3,7 @@ --- // Override lists. #set list(around: none) -#show v: list as "(" + v.items.join(", ") + ")" +#show list: it => "(" + it.items.join(", ") + ")" - A - B @@ -14,12 +14,12 @@ --- // Test full reset. #set heading(size: 1em, strong: false, around: none) -#show heading as [B] +#show heading: [B] A [= Heading] C --- // Test full removal. -#show heading as [] +#show heading: none #set heading(around: none) Where is @@ -29,13 +29,13 @@ my heading? --- // Test integrated example. #set heading(size: 1em) -#show node: heading as { +#show heading: it => { move(dy: -1pt)[📖] h(5pt) - if node.level == 1 { - underline(text(1.25em, blue, node.body)) + if it.level == 1 { + underline(text(1.25em, blue, it.body)) } else { - text(red, node.body) + text(red, it.body) } } @@ -50,10 +50,10 @@ Another text. --- // Test set and show in code blocks. -#show node: heading as { +#show heading: it => { set text(red) - show "ding" as [🛎] - node.body + show "ding": [🛎] + it.body } = Heading @@ -62,12 +62,12 @@ Another text. // Test that scoping works as expected. { let world = [ World ] - show c: "W" as strong(c) + show "W": strong world { set text(blue) - wrap it in { - show "o" as "Ø" + show it => { + show "o": "Ø" it } world @@ -76,22 +76,27 @@ Another text. } --- -#show heading as 1234 +#show heading: [1234] = Heading --- // Error: 25-29 unknown field "page" -#show it: heading as it.page +#show heading: it => it.page = Heading --- -// Error: 10-15 this function cannot be customized with show -#show _: upper as {} +// Error: 7-12 this function cannot be customized with show +#show upper: it => {} + +--- +// Error: 16-20 expected content or function, found integer +#show heading: 1234 += Heading --- // Error: 7-10 expected function, string or regular expression, found color -#show red as [] +#show red: [] --- -// Error: 7-27 show is only allowed directly in code and content blocks -{ 1 + show heading as none } +// Error: 7-25 show is only allowed directly in code and content blocks +{ 1 + show heading: none } diff --git a/tests/typ/style/show-recursive.typ b/tests/typ/style/show-recursive.typ index 9e93739c2..566879af7 100644 --- a/tests/typ/style/show-recursive.typ +++ b/tests/typ/style/show-recursive.typ @@ -2,17 +2,18 @@ --- // Test basic identity. -#show it: heading as it +#show heading: it => it = Heading --- // Test more recipes down the chain. -#show it: list as scale(origin: left, x: 80%, it) -#show heading as [] -#show enum as [] +#show list: scale.with(origin: left, x: 80%) +#show heading: [] +#show enum: [] - Actual - Tight - List += Nope --- // Test recursive base recipe. (Burn it with fire!) @@ -23,11 +24,11 @@ --- // Test show rule in function. #let starwars(body) = [ - #show v: list as { + #show list: it => { stack(dir: ltr, - text(red, v), + text(red, it), 1fr, - scale(x: -100%, text(blue, v)), + scale(x: -100%, text(blue, it)), ) } #body @@ -44,8 +45,8 @@ --- // Test multi-recursion with nested lists. #set rect(inset: 2pt) -#show v: list as rect(stroke: blue, v) -#show v: list as rect(stroke: red, v) +#show list: rect.with(stroke: blue) +#show list: rect.with(stroke: red) - List - Nested @@ -55,8 +56,8 @@ --- // Inner heading is not finalized. Bug? #set heading(around: none) -#show it: heading as it.body -#show heading as [ +#show heading: it => it.body +#show heading: [ = A [ = B ] diff --git a/tests/typ/style/show-text.typ b/tests/typ/style/show-text.typ index 283a28879..457ce9b7e 100644 --- a/tests/typ/style/show-text.typ +++ b/tests/typ/style/show-text.typ @@ -3,22 +3,22 @@ --- // Test classic example. #set text("Roboto") -#show phrase: "Der Spiegel" as smallcaps[#phrase] +#show "Der Spiegel": smallcaps Die Zeitung Der Spiegel existiert. --- // Another classic example. -#show "TeX" as [T#h(-0.145em)#move(dy: 0.233em)[E]#h(-0.135em)X] -#show name: regex("(Lua)?(La)?TeX") as box(text("Latin Modern Roman")[#name]) +#show "TeX": [T#h(-0.145em)#move(dy: 0.233em)[E]#h(-0.135em)X] +#show regex("(Lua)?(La)?TeX"): name => box(text("Latin Modern Roman")[#name]) TeX, LaTeX, LuaTeX and LuaLaTeX! --- // Test out-of-order guarding. -#show "Good" as [Typst!] -#show "Typst" as [Fun!] -#show "Fun" as [Good!] -#show enum as [] +#show "Good": [Typst!] +#show "Typst": [Fun!] +#show "Fun": [Good!] +#show enum: [] Good \ Fun \ @@ -26,32 +26,32 @@ Typst \ --- // Test that replacements happen exactly once. -#show "A" as [BB] -#show "B" as [CC] +#show "A": [BB] +#show "B": [CC] AA (8) --- // Test caseless match and word boundaries. -#show regex("(?i)\bworld\b") as [🌍] +#show regex("(?i)\bworld\b"): [🌍] Treeworld, the World of worlds, is a world. --- // This is a fun one. #set par(justify: true) -#show letter: regex("\S") as rect(inset: 2pt)[#upper(letter)] +#show regex("\S"): letter => rect(inset: 2pt)[#upper(letter)] #lorem(5) --- // See also: https://github.com/mTvare6/hello-world.rs -#show it: regex("(?i)rust") as [#it (🚀)] +#show regex("(?i)rust"): it => [#it (🚀)] Rust is memory-safe and blazingly fast. Let's rewrite everything in rust. --- // Replace worlds but only in lists. -#show node: list as [ - #show "World" as [🌎] - #node +#show list: it => [ + #show "World": [🌎] + #it ] World @@ -60,6 +60,6 @@ World --- // Test absolute path in layout phase. -#show "GRAPH" as image("/res/graph.png") +#show "GRAPH": image("/res/graph.png") The GRAPH has nodes. diff --git a/tests/typ/text/code.typ b/tests/typ/text/code.typ index 4230dd873..d89f1c055 100644 --- a/tests/typ/text/code.typ +++ b/tests/typ/text/code.typ @@ -7,6 +7,7 @@ #lorem(100) #let hi = "Hello World" +#show heading: emph ``` --- diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json index 743513235..8e0907f6e 100644 --- a/tools/support/typst.tmLanguage.json +++ b/tools/support/typst.tmLanguage.json @@ -142,7 +142,7 @@ "captures": { "1": { "name": "punctuation.definition.reference.typst" } } }, { - "begin": "(#)(let|set|show|wrap|apply|select)\\b", + "begin": "(#)(let|set|show)\\b", "end": "\n|(;)|(?=])", "beginCaptures": { "0": { "name": "keyword.other.typst" }, @@ -253,7 +253,7 @@ }, { "name": "keyword.other.typst", - "match": "\\b(let|as|in|from|set|show|wrap|apply|select)\\b" + "match": "\\b(let|as|in|from|set|show)\\b" }, { "name": "keyword.control.conditional.typst", @@ -277,6 +277,11 @@ "name": "entity.name.function.typst", "match": "\\b[[:alpha:]_][[:alnum:]_-]*!?(?=\\[|\\()" }, + { + "comment": "Function name", + "name": "entity.name.function.typst", + "match": "(?<=\\bshow\\s*)\\b[[:alpha:]_][[:alnum:]_-]*(?=\\s*:)" + }, { "comment": "Function arguments", "begin": "(?<=\\b[[:alpha:]_][[:alnum:]_-]*!?)\\(", diff --git a/tools/test-helper/extension.js b/tools/test-helper/extension.js index 985112f05..253c78c71 100644 --- a/tools/test-helper/extension.js +++ b/tools/test-helper/extension.js @@ -38,12 +38,12 @@ function activate(context) { const rerunCmd = vscode.commands.registerCommand("ShortcutMenuBar.testRerun", () => { const uri = vscode.window.activeTextEditor.document.uri - const components = uri.fsPath.split('tests') + const components = uri.fsPath.split(/tests[\/\\]/) const dir = components[0] const subPath = components[1] cp.exec( - `cargo test --manifest-path ${dir}/Cargo.toml --test typeset ${subPath}`, + `cargo test --manifest-path ${dir}/Cargo.toml --all --test tests -- ${subPath}`, (err, stdout, stderr) => { console.log('Ran tests') refreshPanel(stdout, stderr)