summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-07 12:21:12 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-07 12:46:05 +0100
commitefd1853d069fbd1476e82d015da4d0d04cfaccc0 (patch)
tree842b745c134306539d10c61be9485794fe8dc7dc
parenteb951c008beea502042db4a3a0e8d1f8b51f6f52 (diff)
Show it!
- New show rule syntax - Set if syntax - Removed wrap syntax
-rw-r--r--library/src/layout/page.rs2
-rw-r--r--library/src/math/mod.rs17
-rw-r--r--library/src/prelude.rs4
-rw-r--r--library/src/structure/heading.rs18
-rw-r--r--library/src/structure/list.rs34
-rw-r--r--library/src/structure/reference.rs20
-rw-r--r--library/src/structure/table.rs24
-rw-r--r--library/src/text/deco.rs27
-rw-r--r--library/src/text/link.rs52
-rw-r--r--library/src/text/mod.rs44
-rw-r--r--library/src/text/raw.rs26
-rw-r--r--library/src/text/shift.rs16
-rw-r--r--macros/src/lib.rs14
-rw-r--r--src/model/cast.rs11
-rw-r--r--src/model/content.rs33
-rw-r--r--src/model/eval.rs69
-rw-r--r--src/model/func.rs16
-rw-r--r--src/model/items.rs16
-rw-r--r--src/model/ops.rs8
-rw-r--r--src/model/styles.rs107
-rw-r--r--src/model/value.rs2
-rw-r--r--src/syntax/ast.rs135
-rw-r--r--src/syntax/highlight.rs12
-rw-r--r--src/syntax/kind.rs70
-rw-r--r--src/syntax/parsing.rs145
-rw-r--r--src/syntax/tokens.rs2
-rw-r--r--tests/ref/style/set.pngbin20825 -> 24880 bytes
-rw-r--r--tests/ref/style/show-bare.png (renamed from tests/ref/style/wrap.png)bin24846 -> 25853 bytes
-rw-r--r--tests/ref/text/code.pngbin38900 -> 40777 bytes
-rw-r--r--tests/src/tests.rs2
-rw-r--r--tests/typ/base/eval.typ11
-rw-r--r--tests/typ/code/break-continue.typ2
-rw-r--r--tests/typ/code/field.typ4
-rw-r--r--tests/typ/code/import.typ2
-rw-r--r--tests/typ/code/include.typ2
-rw-r--r--tests/typ/graphics/shape-rect.typ4
-rw-r--r--tests/typ/structure/desc.typ2
-rw-r--r--tests/typ/structure/heading.typ6
-rw-r--r--tests/typ/style/set.typ13
-rw-r--r--tests/typ/style/show-bare.typ (renamed from tests/typ/style/wrap.typ)23
-rw-r--r--tests/typ/style/show-node.typ45
-rw-r--r--tests/typ/style/show-recursive.typ23
-rw-r--r--tests/typ/style/show-text.typ32
-rw-r--r--tests/typ/text/code.typ1
-rw-r--r--tools/support/typst.tmLanguage.json9
-rw-r--r--tools/test-helper/extension.js4
46 files changed, 531 insertions, 578 deletions
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index e1af6ec5..c308571c 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -200,7 +200,7 @@ impl Cast<Spanned<Value>> for Marginal {
fn cast(value: Spanned<Value>) -> StrResult<Self> {
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 dae869ed..0fad2939 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<BlockSpacing> = Some(Ratio::one().into());
+
+ fn field(&self, name: &str) -> Option<Value> {
+ 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<Value> {
- None
- }
-
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
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<dyn World>,
diff --git a/library/src/prelude.rs b/library/src/prelude.rs
index 11095c67..f51b826f 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 62a67000..46e98c18 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<Value> {
match name {
@@ -81,11 +75,19 @@ impl Show for HeadingNode {
_ => None,
}
}
+}
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+impl Show for HeadingNode {
+ fn unguard_parts(&self, sel: Selector) -> Content {
+ Self { body: self.body.unguard(sel), ..*self }.pack()
+ }
+
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(BlockNode(self.body.clone()).pack())
}
+}
+impl Finalize for HeadingNode {
fn finalize(
&self,
world: Tracked<dyn World>,
diff --git a/library/src/structure/list.rs b/library/src/structure/list.rs
index a5e1380a..499207a4 100644
--- a/library/src/structure/list.rs
+++ b/library/src/structure/list.rs
@@ -22,7 +22,7 @@ pub type EnumNode = ListNode<ENUM>;
/// A description list.
pub type DescNode = ListNode<DESC>;
-#[node(Show)]
+#[node(Show, Finalize)]
impl<const L: ListKind> ListNode<L> {
/// How the list is labelled.
#[property(referenced)]
@@ -80,16 +80,6 @@ impl<const L: ListKind> ListNode<L> {
}
.pack())
}
-}
-
-impl<const L: ListKind> Show for ListNode<L> {
- fn unguard_parts(&self, sel: Selector) -> Content {
- Self {
- items: self.items.map(|item| item.unguard(sel)),
- ..*self
- }
- .pack()
- }
fn field(&self, name: &str) -> Option<Value> {
match name {
@@ -101,8 +91,18 @@ impl<const L: ListKind> Show for ListNode<L> {
_ => None,
}
}
+}
- fn realize(
+impl<const L: ListKind> Show for ListNode<L> {
+ fn unguard_parts(&self, sel: Selector) -> Content {
+ Self {
+ items: self.items.map(|item| item.unguard(sel)),
+ ..*self
+ }
+ .pack()
+ }
+
+ fn show(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
@@ -140,7 +140,7 @@ impl<const L: ListKind> Show for ListNode<L> {
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<const L: ListKind> Show for ListNode<L> {
}
.pack())
}
+}
+impl<const L: ListKind> Finalize for ListNode<L> {
fn finalize(
&self,
_: Tracked<dyn World>,
@@ -312,14 +314,14 @@ impl Label {
) -> SourceResult<Content> {
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 56f8b8e3..18f4eecb 100644
--- a/library/src/structure/reference.rs
+++ b/library/src/structure/reference.rs
@@ -8,23 +8,23 @@ pub struct RefNode(pub EcoString);
#[node(Show)]
impl RefNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("label")?).pack())
- }
-}
-
-impl Show for RefNode {
- fn unguard_parts(&self, _: Selector) -> Content {
- Self(self.0.clone()).pack()
+ Ok(Self(args.expect("target")?).pack())
}
fn field(&self, name: &str) -> Option<Value> {
match name {
- "label" => Some(Value::Str(self.0.clone().into())),
+ "target" => Some(Value::Str(self.0.clone().into())),
_ => None,
}
}
+}
+
+impl Show for RefNode {
+ fn unguard_parts(&self, _: Selector) -> Content {
+ Self(self.0.clone()).pack()
+ }
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
- Ok(TextNode(format_eco!("@{}", self.0)).pack())
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+ Ok(TextNode::packed(format_eco!("@{}", self.0)))
}
}
diff --git a/library/src/structure/table.rs b/library/src/structure/table.rs
index 722f11e6..fbf1c7c0 100644
--- a/library/src/structure/table.rs
+++ b/library/src/structure/table.rs
@@ -12,7 +12,7 @@ pub struct TableNode {
pub cells: Vec<Content>,
}
-#[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<Value> {
+ 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<Value> {
- match name {
- "cells" => Some(Value::Array(
- self.cells.iter().cloned().map(Value::Content).collect(),
- )),
- _ => None,
- }
- }
-
- fn realize(
+ fn show(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
@@ -106,7 +106,9 @@ impl Show for TableNode {
}
.pack())
}
+}
+impl Finalize for TableNode {
fn finalize(
&self,
_: Tracked<dyn World>,
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index 10f3db38..fa0f05a7 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -37,12 +37,6 @@ impl<const L: DecoLine> DecoNode<L> {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack())
}
-}
-
-impl<const L: DecoLine> Show for DecoNode<L> {
- fn unguard_parts(&self, sel: Selector) -> Content {
- Self(self.0.unguard(sel)).pack()
- }
fn field(&self, name: &str) -> Option<Value> {
match name {
@@ -50,12 +44,14 @@ impl<const L: DecoLine> Show for DecoNode<L> {
_ => None,
}
}
+}
+
+impl<const L: DecoLine> Show for DecoNode<L> {
+ fn unguard_parts(&self, sel: Selector) -> Content {
+ Self(self.0.unguard(sel)).pack()
+ }
- fn realize(
- &self,
- _: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Content> {
+ fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
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<Self>;
+
+ 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 82abe5cd..4312559e 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<Value> {
match name {
@@ -57,25 +47,33 @@ impl Show for LinkNode {
_ => None,
}
}
+}
+
+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 realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
- 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()
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+ 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);
}
- Destination::Internal(_) => Content::empty(),
- })
- .styled(TextNode::LINK, Some(self.dest.clone())))
+ 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<dyn World>,
@@ -83,6 +81,8 @@ impl Show for LinkNode {
mut realized: Content,
) -> SourceResult<Content> {
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 6643f821..86c6884a 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<EcoString>) -> 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<Content> {
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<Value> {
match name {
@@ -499,8 +500,14 @@ impl Show for StrongNode {
_ => None,
}
}
+}
+
+impl Show for StrongNode {
+ fn unguard_parts(&self, sel: Selector) -> Content {
+ Self(self.0.unguard(sel)).pack()
+ }
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
}
}
@@ -514,12 +521,6 @@ impl EmphNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
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<Value> {
match name {
@@ -527,8 +528,14 @@ impl Show for EmphNode {
_ => None,
}
}
+}
+
+impl Show for EmphNode {
+ fn unguard_parts(&self, sel: Selector) -> Content {
+ Self(self.0.unguard(sel)).pack()
+ }
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
}
}
@@ -544,12 +551,3 @@ impl Fold for Toggle {
!outer
}
}
-
-impl Fold for Decoration {
- type Output = Vec<Self>;
-
- 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 31f1517e..5a98cf3b 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<Value> {
match name {
@@ -55,12 +49,14 @@ impl Show for RawNode {
_ => None,
}
}
+}
- fn realize(
- &self,
- _: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Content> {
+impl Show for RawNode {
+ fn unguard_parts(&self, _: Selector) -> Content {
+ Self { text: self.text.clone(), ..*self }.pack()
+ }
+
+ fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
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<dyn World>,
@@ -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 1117cc00..0f654b5a 100644
--- a/library/src/text/shift.rs
+++ b/library/src/text/shift.rs
@@ -33,12 +33,6 @@ impl<const S: ShiftKind> ShiftNode<S> {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack())
}
-}
-
-impl<const S: ShiftKind> Show for ShiftNode<S> {
- fn unguard_parts(&self, _: Selector) -> Content {
- Self(self.0.clone()).pack()
- }
fn field(&self, name: &str) -> Option<Value> {
match name {
@@ -46,8 +40,14 @@ impl<const S: ShiftKind> Show for ShiftNode<S> {
_ => None,
}
}
+}
+
+impl<const S: ShiftKind> Show for ShiftNode<S> {
+ fn unguard_parts(&self, _: Selector) -> Content {
+ Self(self.0.clone()).pack()
+ }
- fn realize(
+ fn show(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
@@ -56,7 +56,7 @@ impl<const S: ShiftKind> Show for ShiftNode<S> {
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 8e44a8c3..eaa929dc 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<Value> {
+ None
+ }
+ }
+ });
+
let items: syn::punctuated::Punctuated<Ident, syn::Token![,]> =
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::<Self>()
}
+
+ #vtable
}
#(#key_modules)*
diff --git a/src/model/cast.rs b/src/model/cast.rs
index cbb2952d..7a466b72 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 7b09c697..0257f4da 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<EcoString>) -> Self {
- item!(text)(text.into())
- }
-
/// Create a new sequence node from multiples nodes.
pub fn sequence(seq: Vec<Self>) -> Self {
match seq.as_slice() {
@@ -65,6 +61,11 @@ impl Content {
Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::<T>()
}
+ /// Access a field on this content.
+ pub fn field(&self, name: &str) -> Option<Value> {
+ self.0.field(name)
+ }
+
/// Whether this content has the given capability.
pub fn has<C>(&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<dyn World>,
+ recipe: Recipe,
+ ) -> SourceResult<Self> {
+ 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::<StyledNode>() {
@@ -242,6 +256,9 @@ pub trait Node: 'static {
where
Self: Sized;
+ /// Access a field on this node.
+ fn field(&self, name: &str) -> Option<Value>;
+
/// A unique identifier of the node type.
fn id(&self) -> NodeId;
diff --git a/src/model/eval.rs b/src/model/eval.rs
index 16e66818..fd43c4c3 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::<dyn Show>()
- .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<Self::Output> {
+ if let Some(condition) = self.condition() {
+ if !condition.eval(vm)?.cast::<bool>().at(condition.span())? {
+ return Ok(StyleMap::new());
+ }
+ }
let target = self.target();
let target = target.eval(vm)?.cast::<Func>().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<Self::Output> {
- // Evaluate the target function.
- let pattern = self.pattern();
- let pattern = pattern.eval(vm)?.cast::<Pattern>().at(pattern.span())?;
-
- // Collect captured variables.
- let captured = {
- let mut visitor = CapturesVisitor::new(&vm.scopes);
- visitor.visit(self.as_untyped());
- visitor.finish()
- };
-
- // Define parameters.
- let mut params = vec![];
- if let Some(binding) = self.binding() {
- params.push((binding.take(), None));
- }
+ let pattern = self
+ .pattern()
+ .map(|pattern| pattern.eval(vm)?.cast::<Pattern>().at(pattern.span()))
+ .transpose()?;
- // 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,
- });
+ let transform = self.transform();
+ let span = transform.span();
+ let transform = transform.eval(vm)?.cast::<Transform>().at(span)?;
- 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<Module> {
Ok(module)
}
-impl Eval for ast::BreakStmt {
+impl Eval for ast::LoopBreak {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -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<Self::Output> {
@@ -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<Self::Output> {
diff --git a/src/model/func.rs b/src/model/func.rs
index 456b6aa6..8cedb158 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 164d9602..e9c23c26 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<usize>, 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<Content>, 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<Content>, sup: Option<Content>) -> 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 9d55fa63..0110fb96 100644
--- a/src/model/ops.rs
+++ b/src/model/ops.rs
@@ -19,8 +19,8 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
(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<Value> {
(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 9463e55e..3800490b 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::<dyn Show>()
.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::<dyn Show>().unwrap().finalize(world, self, content)?,
- );
+ if let Some(node) = node.to::<dyn Finalize>() {
+ if let Some(content) = realized {
+ realized = Some(node.finalize(world, self, content)?);
+ }
}
}
}
@@ -974,18 +974,20 @@ impl Fold for PartialStroke<Abs> {
/// 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<Func>,
+ /// The span errors are reported with.
+ pub span: Span,
+ /// The pattern that the rule applies to.
+ pub pattern: Option<Pattern>,
+ /// 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<Option<Content>> {
let content = match (target, &self.pattern) {
- (Target::Node(node), &Pattern::Node(id)) if node.id() == id => {
- let node = node.to::<dyn Show>().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::<dyn Show>().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<F>(&self, world: Tracked<dyn World>, arg: F) -> SourceResult<Content>
- 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<F>(
+ &self,
+ world: Tracked<dyn World>,
+ span: Span,
+ arg: F,
+ ) -> SourceResult<Content>
+ 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<Value>;
-
/// The base recipe for this node that is executed if there is no
/// user-defined show rule.
- fn realize(
+ fn show(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
) -> SourceResult<Content>;
+}
+/// 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<dyn World>,
styles: StyleChain,
realized: Content,
- ) -> SourceResult<Content> {
- Ok(realized)
- }
+ ) -> SourceResult<Content>;
}
diff --git a/src/model/value.rs b/src/model/value.rs
index 825e6f55..9e6968fa 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 61b8f0e6..1b0e8985 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<usize> {
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<Expr> {
+ 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<Ident> {
- 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<Expr> {
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<Expr> {
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 d5345fab..34cce4c0 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 1568693b..1282b592 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<RawKind>),
@@ -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 fecc527c..acf76b7e 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<NodeKind>) {
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<NodeKind> {
let marker = p.marker();
if p.eat_if(NodeKind::Dots) {
@@ -806,8 +783,6 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
}
}
-/// 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)
+ args(p)?;
+ if p.eat_if(NodeKind::If) {
+ expr(p)?;
+ }
+ Ok(())
})
}
-/// Parse a show expression.
-fn show_expr(p: &mut Parser) -> ParseResult {
+fn show_rule(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"),
- });
expr(p)?;
}
- p.expect(NodeKind::As)?;
- expr(p)
- })
-}
-
-/// 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)
+ 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 f18bb780..b27832c5 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -613,12 +613,10 @@ fn keyword(ident: &str) -> Option<NodeKind> {
"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 502d74b5..6e154980 100644
--- a/tests/ref/style/set.png
+++ b/tests/ref/style/set.png
Binary files differ
diff --git a/tests/ref/style/wrap.png b/tests/ref/style/show-bare.png
index 7fb8dfc5..ff746d26 100644
--- a/tests/ref/style/wrap.png
+++ b/tests/ref/style/show-bare.png
Binary files differ
diff --git a/tests/ref/text/code.png b/tests/ref/text/code.png
index c4e94a73..e0387870 100644
--- a/tests/ref/text/code.png
+++ b/tests/ref/text/code.png
Binary files differ
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index 389938a4..aa9beaee 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -38,7 +38,7 @@ fn main() {
// cache, a deterministic order is important for reproducibility.
for entry in WalkDir::new("typ").sort_by_file_name() {
let entry = entry.unwrap();
- if entry.depth() <= 1 {
+ if entry.depth() == 0 {
continue;
}
diff --git a/tests/typ/base/eval.typ b/tests/typ/base/eval.typ
index 86b1f0c4..668264d3 100644
--- a/tests/typ/base/eval.typ
+++ b/tests/typ/base/eval.typ
@@ -9,7 +9,7 @@
---
#set raw(around: none)
-#show it: raw as text("IBM Plex Sans", eval(it.text))
+#show raw: it => 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 2415cb8f..fb3222fa 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 b63a8768..abea87fb 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 b554d6e7..5291af39 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 cd3328a2..e862adac 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 7d110180..94686da2 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 e12bbd16..af1b2986 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 d37d8796..d08b3687 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 25dc1b81..2864b81b 100644
--- a/tests/typ/style/set.typ
+++ b/tests/typ/style/set.typ
@@ -49,5 +49,18 @@ Hello *{x}*
+ 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
index e37c4bc9..2dba742f 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 b35ab4c4..56b7e34a 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 9e93739c..566879af 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 283a2887..457ce9b7 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 4230dd87..d89f1c05 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 74351323..8e0907f6 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",
@@ -278,6 +278,11 @@
"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:]_-]*!?)\\(",
"end": "\\)",
diff --git a/tools/test-helper/extension.js b/tools/test-helper/extension.js
index 985112f0..253c78c7 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)