summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval/capture.rs13
-rw-r--r--src/eval/content.rs23
-rw-r--r--src/eval/mod.rs25
-rw-r--r--src/eval/raw.rs1
-rw-r--r--src/eval/show.rs28
-rw-r--r--src/eval/styles.rs21
-rw-r--r--src/library/layout/grid.rs6
-rw-r--r--src/library/math/mod.rs20
-rw-r--r--src/library/structure/heading.rs23
-rw-r--r--src/library/structure/list.rs49
-rw-r--r--src/library/structure/table.rs31
-rw-r--r--src/library/text/deco.rs31
-rw-r--r--src/library/text/link.rs43
-rw-r--r--src/library/text/mod.rs30
-rw-r--r--src/library/text/raw.rs25
-rw-r--r--src/parse/mod.rs17
-rw-r--r--src/syntax/ast.rs21
-rw-r--r--src/syntax/highlight.rs18
-rw-r--r--src/syntax/mod.rs2
-rw-r--r--tests/typ/style/show.typ30
20 files changed, 293 insertions, 164 deletions
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
index 4e8d7604..8e54122f 100644
--- a/src/eval/capture.rs
+++ b/src/eval/capture.rs
@@ -88,6 +88,14 @@ 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(Expr::Show(show)) => {
+ self.visit(show.target().as_red());
+ self.bind(show.binding());
+ self.visit(show.body().as_red());
+ }
+
// 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.
@@ -162,6 +170,11 @@ mod tests {
test("{(..x) => x + y}", &["y"]);
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"]);
+
// For loop.
test("#for x in y { x + z }", &["y", "z"]);
test("#for x, y in y { x + y }", &["y"]);
diff --git a/src/eval/content.rs b/src/eval/content.rs
index 605abe51..098bfbfc 100644
--- a/src/eval/content.rs
+++ b/src/eval/content.rs
@@ -320,7 +320,7 @@ struct ListBuilder<'a> {
styles: StyleChain<'a>,
kind: ListKind,
items: Vec<ListItem>,
- wide: bool,
+ tight: bool,
staged: Vec<(&'a Content, StyleChain<'a>)>,
}
@@ -356,15 +356,15 @@ impl<'a> Builder<'a> {
return Ok(());
}
Content::List(item) if builder.kind == UNORDERED => {
- builder.wide |=
- builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak);
+ builder.tight &=
+ builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak);
builder.staged.clear();
builder.items.push(item.clone());
return Ok(());
}
Content::Enum(item) if builder.kind == ORDERED => {
- builder.wide |=
- builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak);
+ builder.tight &=
+ builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak);
builder.staged.clear();
builder.items.push(item.clone());
return Ok(());
@@ -430,7 +430,7 @@ impl<'a> Builder<'a> {
styles,
kind: UNORDERED,
items: vec![item.clone()],
- wide: false,
+ tight: true,
staged: vec![],
});
}
@@ -439,7 +439,7 @@ impl<'a> Builder<'a> {
styles,
kind: ORDERED,
items: vec![item.clone()],
- wide: false,
+ tight: true,
staged: vec![],
});
}
@@ -454,7 +454,8 @@ impl<'a> Builder<'a> {
}
Content::Show(node) => {
let id = node.id();
- let content = node.show(ctx, styles)?;
+ let realized = styles.realize(ctx, node)?;
+ let content = node.show(ctx, styles, realized)?;
let stored = self.tpa.alloc(content);
self.process(ctx, stored, styles.unscoped(id))?;
}
@@ -532,14 +533,14 @@ impl<'a> Builder<'a> {
/// Finish the currently built list.
fn finish_list(&mut self, ctx: &mut Context) -> TypResult<()> {
- let ListBuilder { styles, kind, items, wide, staged } = match self.list.take() {
+ let ListBuilder { styles, kind, items, tight, staged } = match self.list.take() {
Some(list) => list,
None => return Ok(()),
};
let content = match kind {
- UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, wide, items }),
- ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, wide, items }),
+ UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, tight, items }),
+ ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, tight, items }),
};
let stored = self.tpa.alloc(content);
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index d9651cce..7e0d3b15 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -624,13 +624,30 @@ impl Eval for ShowExpr {
type Output = StyleMap;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
+ // Evaluate the target function.
let target = self.target();
let target_span = target.span();
let target = target.eval(ctx, scp)?.cast::<Func>().at(target_span)?;
- let recipe = self.recipe();
- let recipe_span = recipe.span();
- let recipe = recipe.eval(ctx, scp)?.cast::<Func>().at(recipe_span)?;
- Ok(target.show(recipe, recipe_span).at(target_span)?)
+
+ // Collect captured variables.
+ let captured = {
+ let mut visitor = CapturesVisitor::new(scp);
+ visitor.visit(self.as_red());
+ visitor.finish()
+ };
+
+ // Define the recipe function.
+ let body = self.body();
+ let body_span = body.span();
+ let recipe = Func::from_closure(Closure {
+ name: None,
+ captured,
+ params: vec![(self.binding().take(), None)],
+ sink: None,
+ body,
+ });
+
+ Ok(target.show(recipe, body_span).at(target_span)?)
}
}
diff --git a/src/eval/raw.rs b/src/eval/raw.rs
index a83c363f..6792c491 100644
--- a/src/eval/raw.rs
+++ b/src/eval/raw.rs
@@ -110,7 +110,6 @@ impl Resolve for RawStroke {
}
}
-// This faciliates RawStroke => Stroke.
impl Fold for RawStroke<Length> {
type Output = Self;
diff --git a/src/eval/show.rs b/src/eval/show.rs
index 383497ba..f8d98d52 100644
--- a/src/eval/show.rs
+++ b/src/eval/show.rs
@@ -3,15 +3,24 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
use std::sync::Arc;
-use super::{Content, StyleChain};
+use super::{Content, Dict, StyleChain};
use crate::diag::TypResult;
use crate::util::Prehashed;
use crate::Context;
/// A node that can be realized given some styles.
pub trait Show: 'static {
- /// Realize this node in the given styles.
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content>;
+ /// Encode this node into a dictionary.
+ fn encode(&self) -> Dict;
+
+ /// Show this node in the given styles and optionally given the realization
+ /// of a show rule.
+ fn show(
+ &self,
+ ctx: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content>;
/// Convert to a packed show node.
fn pack(self) -> ShowNode
@@ -42,8 +51,17 @@ impl ShowNode {
}
impl Show for ShowNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- self.0.show(ctx, styles)
+ fn encode(&self) -> Dict {
+ self.0.encode()
+ }
+
+ fn show(
+ &self,
+ ctx: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ self.0.show(ctx, styles, realized)
}
fn pack(self) -> ShowNode {
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index f147d8cf..b666c85d 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -4,7 +4,7 @@ use std::hash::Hash;
use std::marker::PhantomData;
use std::sync::Arc;
-use super::{Args, Content, Func, Layout, Node, Smart, Span, Value};
+use super::{Args, Content, Func, Layout, Node, Show, ShowNode, Smart, Span, Value};
use crate::diag::{At, TypResult};
use crate::geom::{Numeric, Relative, Sides, Spec};
use crate::library::layout::PageNode;
@@ -510,19 +510,20 @@ impl<'a> StyleChain<'a> {
K::get(self, self.values(key))
}
- /// Execute and return the result of a user recipe for a node if there is
- /// any.
- pub fn show<T, I>(self, ctx: &mut Context, values: I) -> TypResult<Option<Content>>
- where
- T: Node,
- I: IntoIterator<Item = Value>,
- {
+ /// Realize a node with a user recipe.
+ pub fn realize(
+ self,
+ ctx: &mut Context,
+ node: &ShowNode,
+ ) -> TypResult<Option<Content>> {
+ let id = node.id();
if let Some(recipe) = self
.entries()
.filter_map(Entry::recipe)
- .find(|recipe| recipe.node == TypeId::of::<T>())
+ .find(|recipe| recipe.node == id)
{
- let args = Args::from_values(recipe.span, values);
+ let dict = node.encode();
+ let args = Args::from_values(recipe.span, [Value::Dict(dict)]);
Ok(Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?))
} else {
Ok(None)
diff --git a/src/library/layout/grid.rs b/src/library/layout/grid.rs
index 4908d4d8..afd08aee 100644
--- a/src/library/layout/grid.rs
+++ b/src/library/layout/grid.rs
@@ -8,7 +8,7 @@ pub struct GridNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid.
- pub children: Vec<LayoutNode>,
+ pub cells: Vec<LayoutNode>,
}
#[node]
@@ -25,7 +25,7 @@ impl GridNode {
column_gutter.unwrap_or_else(|| base_gutter.clone()),
row_gutter.unwrap_or(base_gutter),
),
- children: args.all()?,
+ cells: args.all()?,
}))
}
}
@@ -41,7 +41,7 @@ impl Layout for GridNode {
let layouter = GridLayouter::new(
self.tracks.as_deref(),
self.gutter.as_deref(),
- &self.children,
+ &self.cells,
regions,
styles,
);
diff --git a/src/library/math/mod.rs b/src/library/math/mod.rs
index e6548438..587949d7 100644
--- a/src/library/math/mod.rs
+++ b/src/library/math/mod.rs
@@ -28,11 +28,21 @@ impl MathNode {
}
impl Show for MathNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- let args = [Value::Str(self.formula.clone()), Value::Bool(self.display)];
- let mut content = styles
- .show::<Self, _>(ctx, args)?
- .unwrap_or_else(|| Content::Text(self.formula.trim().into()));
+ fn encode(&self) -> Dict {
+ dict! {
+ "formula" => Value::Str(self.formula.clone()),
+ "display" => Value::Bool(self.display)
+ }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ let mut content =
+ realized.unwrap_or_else(|| Content::Text(self.formula.trim().into()));
let mut map = StyleMap::new();
if let Smart::Custom(family) = styles.get(Self::FAMILY) {
diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs
index 07e5e662..a352cc92 100644
--- a/src/library/structure/heading.rs
+++ b/src/library/structure/heading.rs
@@ -56,21 +56,26 @@ impl HeadingNode {
}
impl Show for HeadingNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
+ fn encode(&self) -> Dict {
+ dict! {
+ "level" => Value::Int(self.level as i64),
+ "body" => Value::Content(self.body.clone()),
+ }
+ }
+
+ fn show(
+ &self,
+ ctx: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
macro_rules! resolve {
($key:expr) => {
styles.get($key).resolve(ctx, self.level)?
};
}
- let args = [
- Value::Int(self.level as i64),
- Value::Content(self.body.clone()),
- ];
-
- let mut body = styles
- .show::<Self, _>(ctx, args)?
- .unwrap_or_else(|| self.body.clone());
+ let mut body = realized.unwrap_or_else(|| self.body.clone());
let mut map = StyleMap::new();
map.set(TextNode::SIZE, resolve!(Self::SIZE));
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
index 10dcfb7b..c59b443d 100644
--- a/src/library/structure/list.rs
+++ b/src/library/structure/list.rs
@@ -10,9 +10,9 @@ use crate::library::utility::Numbering;
pub struct ListNode<const L: ListKind = UNORDERED> {
/// Where the list starts.
pub start: usize,
- /// If true, there is paragraph spacing between the items, if false
+ /// If false, there is paragraph spacing between the items, if true
/// there is list spacing between the items.
- pub wide: bool,
+ pub tight: bool,
/// The individual bulleted or numbered items.
pub items: Vec<ListItem>,
}
@@ -55,7 +55,7 @@ impl<const L: ListKind> ListNode<L> {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self {
start: args.named("start")?.unwrap_or(1),
- wide: args.named("wide")?.unwrap_or(false),
+ tight: args.named("tight")?.unwrap_or(true),
items: args
.all()?
.into_iter()
@@ -66,30 +66,47 @@ impl<const L: ListKind> ListNode<L> {
}
impl<const L: ListKind> Show for ListNode<L> {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- let args = self.items.iter().map(|item| Value::Content((*item.body).clone()));
- let content = if let Some(content) = styles.show::<Self, _>(ctx, args)? {
+ fn encode(&self) -> Dict {
+ dict! {
+ "start" => Value::Int(self.start as i64),
+ "tight" => Value::Bool(self.tight),
+ "items" => Value::Array(
+ self.items
+ .iter()
+ .map(|item| Value::Content((*item.body).clone()))
+ .collect()
+ ),
+ }
+ }
+
+ fn show(
+ &self,
+ ctx: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ let content = if let Some(content) = realized {
content
} else {
- let mut children = vec![];
+ let mut cells = vec![];
let mut number = self.start;
let label = styles.get(Self::LABEL);
for item in &self.items {
number = item.number.unwrap_or(number);
- children.push(LayoutNode::default());
- children.push(label.resolve(ctx, L, number)?.pack());
- children.push(LayoutNode::default());
- children.push((*item.body).clone().pack());
+ cells.push(LayoutNode::default());
+ cells.push(label.resolve(ctx, L, number)?.pack());
+ cells.push(LayoutNode::default());
+ cells.push((*item.body).clone().pack());
number += 1;
}
let leading = styles.get(ParNode::LEADING);
- let spacing = if self.wide {
- styles.get(ParNode::SPACING)
- } else {
+ let spacing = if self.tight {
styles.get(Self::SPACING)
+ } else {
+ styles.get(ParNode::SPACING)
};
let gutter = leading + spacing;
@@ -104,7 +121,7 @@ impl<const L: ListKind> Show for ListNode<L> {
TrackSizing::Auto,
]),
gutter: Spec::with_y(vec![TrackSizing::Relative(gutter.into())]),
- children,
+ cells,
})
};
@@ -127,7 +144,7 @@ impl<const L: ListKind> Show for ListNode<L> {
impl<const L: ListKind> From<ListItem> for ListNode<L> {
fn from(item: ListItem) -> Self {
- Self { items: vec![item], wide: false, start: 1 }
+ Self { items: vec![item], tight: true, start: 1 }
}
}
diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs
index 96d3bd5b..aefd01b5 100644
--- a/src/library/structure/table.rs
+++ b/src/library/structure/table.rs
@@ -9,7 +9,7 @@ pub struct TableNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in the table.
- pub children: Vec<Content>,
+ pub cells: Vec<Content>,
}
#[node(showable)]
@@ -37,7 +37,7 @@ impl TableNode {
column_gutter.unwrap_or_else(|| base_gutter.clone()),
row_gutter.unwrap_or(base_gutter),
),
- children: args.all()?,
+ cells: args.all()?,
}))
}
@@ -53,9 +53,24 @@ impl TableNode {
}
impl Show for TableNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- let args = self.children.iter().map(|child| Value::Content(child.clone()));
- if let Some(content) = styles.show::<Self, _>(ctx, args)? {
+ fn encode(&self) -> Dict {
+ dict! {
+ "cells" => Value::Array(
+ self.cells
+ .iter()
+ .map(|cell| Value::Content(cell.clone()))
+ .collect()
+ ),
+ }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ if let Some(content) = realized {
return Ok(content);
}
@@ -65,8 +80,8 @@ impl Show for TableNode {
let padding = styles.get(Self::PADDING);
let cols = self.tracks.x.len().max(1);
- let children = self
- .children
+ let cells = self
+ .cells
.iter()
.cloned()
.enumerate()
@@ -90,7 +105,7 @@ impl Show for TableNode {
Ok(Content::block(GridNode {
tracks: self.tracks.clone(),
gutter: self.gutter.clone(),
- children,
+ cells,
}))
}
}
diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs
index 7481b836..9fe4e65a 100644
--- a/src/library/text/deco.rs
+++ b/src/library/text/deco.rs
@@ -43,18 +43,25 @@ impl<const L: DecoLine> DecoNode<L> {
}
impl<const L: DecoLine> Show for DecoNode<L> {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- Ok(styles
- .show::<Self, _>(ctx, [Value::Content(self.0.clone())])?
- .unwrap_or_else(|| {
- self.0.clone().styled(TextNode::DECO, Decoration {
- line: L,
- stroke: styles.get(Self::STROKE).unwrap_or_default(),
- offset: styles.get(Self::OFFSET),
- extent: styles.get(Self::EXTENT),
- evade: styles.get(Self::EVADE),
- })
- }))
+ fn encode(&self) -> Dict {
+ dict! { "body" => Value::Content(self.0.clone()) }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ Ok(realized.unwrap_or_else(|| {
+ self.0.clone().styled(TextNode::DECO, Decoration {
+ line: L,
+ stroke: styles.get(Self::STROKE).unwrap_or_default(),
+ offset: styles.get(Self::OFFSET),
+ extent: styles.get(Self::EXTENT),
+ evade: styles.get(Self::EVADE),
+ })
+ }))
}
}
diff --git a/src/library/text/link.rs b/src/library/text/link.rs
index 3ef7011d..d1e5eb8e 100644
--- a/src/library/text/link.rs
+++ b/src/library/text/link.rs
@@ -28,24 +28,31 @@ impl LinkNode {
}
impl Show for LinkNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- let args = [Value::Str(self.url.clone()), match &self.body {
- Some(body) => Value::Content(body.clone()),
- None => Value::None,
- }];
-
- let mut body = styles
- .show::<Self, _>(ctx, args)?
- .or_else(|| self.body.clone())
- .unwrap_or_else(|| {
- let url = &self.url;
- let mut text = url.as_str();
- for prefix in ["mailto:", "tel:"] {
- text = text.trim_start_matches(prefix);
- }
- let shorter = text.len() < url.len();
- Content::Text(if shorter { text.into() } else { url.clone() })
- });
+ fn encode(&self) -> Dict {
+ dict! {
+ "url" => Value::Str(self.url.clone()),
+ "body" => match &self.body {
+ Some(body) => Value::Content(body.clone()),
+ None => Value::None,
+ },
+ }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ let mut body = realized.or_else(|| self.body.clone()).unwrap_or_else(|| {
+ let url = &self.url;
+ let mut text = url.as_str();
+ for prefix in ["mailto:", "tel:"] {
+ text = text.trim_start_matches(prefix);
+ }
+ let shorter = text.len() < url.len();
+ Content::Text(if shorter { text.into() } else { url.clone() })
+ });
let mut map = StyleMap::new();
map.set(TextNode::LINK, Some(self.url.clone()));
diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs
index bde553e2..e477e76d 100644
--- a/src/library/text/mod.rs
+++ b/src/library/text/mod.rs
@@ -471,10 +471,17 @@ impl StrongNode {
}
impl Show for StrongNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- Ok(styles
- .show::<Self, _>(ctx, [Value::Content(self.0.clone())])?
- .unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, Toggle)))
+ fn encode(&self) -> Dict {
+ dict! { "body" => Value::Content(self.0.clone()) }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ _: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ Ok(realized.unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, Toggle)))
}
}
@@ -490,9 +497,16 @@ impl EmphNode {
}
impl Show for EmphNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- Ok(styles
- .show::<Self, _>(ctx, [Value::Content(self.0.clone())])?
- .unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, Toggle)))
+ fn encode(&self) -> Dict {
+ dict! { "body" => Value::Content(self.0.clone()) }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ _: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
+ Ok(realized.unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, Toggle)))
}
}
diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs
index db97da07..cc225bed 100644
--- a/src/library/text/raw.rs
+++ b/src/library/text/raw.rs
@@ -43,7 +43,19 @@ impl RawNode {
}
impl Show for RawNode {
- fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
+ fn encode(&self) -> Dict {
+ dict! {
+ "text" => Value::Str(self.text.clone()),
+ "block" => Value::Bool(self.block)
+ }
+ }
+
+ fn show(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Option<Content>,
+ ) -> TypResult<Content> {
let lang = styles.get(Self::LANG).as_ref();
let foreground = THEME
.settings
@@ -52,16 +64,7 @@ impl Show for RawNode {
.unwrap_or(Color::BLACK)
.into();
- let args = [
- Value::Str(self.text.clone()),
- match lang {
- Some(lang) => Value::Str(lang.clone()),
- None => Value::None,
- },
- Value::Bool(self.block),
- ];
-
- let mut content = if let Some(content) = styles.show::<Self, _>(ctx, args)? {
+ let mut content = if let Some(content) = realized {
content
} else if matches!(
lang.map(|s| s.to_lowercase()).as_deref(),
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index c387de0c..b27af08f 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -809,19 +809,10 @@ fn show_expr(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ShowExpr, |p| {
p.eat_assert(&NodeKind::Show);
ident(p)?;
- if !p.at(&NodeKind::LeftParen) {
- p.expected_found("parameter list");
- return Err(ParseError);
- }
- p.perform(NodeKind::ClosureExpr, |p| {
- let marker = p.marker();
- p.start_group(Group::Paren);
- collection(p);
- p.end_group();
- params(p, marker);
- p.eat_expect(&NodeKind::As)?;
- expr(p)
- })
+ p.eat_expect(&NodeKind::Colon)?;
+ ident(p)?;
+ p.eat_expect(&NodeKind::As)?;
+ expr(p)
})
}
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 8af359bf..82bb7d56 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -249,7 +249,7 @@ pub enum Expr {
Let(LetExpr),
/// A set expression: `set text(...)`.
Set(SetExpr),
- /// A show expression: `show heading(body) as [*{body}*]`.
+ /// A show expression: `show node: heading as [*{nody.body}*]`.
Show(ShowExpr),
/// A wrap expression: `wrap body in columns(2, body)`.
Wrap(WrapExpr),
@@ -999,19 +999,28 @@ impl SetExpr {
}
node! {
- /// A show expression: `show heading(body) as [*{body}*]`.
+ /// A show expression: `show node: heading as [*{nody.body}*]`.
ShowExpr
}
impl ShowExpr {
+ /// The binding to assign to.
+ pub fn binding(&self) -> Ident {
+ self.0.cast_first_child().expect("show rule is missing binding")
+ }
+
/// The function to customize with this show rule.
pub fn target(&self) -> Ident {
- self.0.cast_first_child().expect("show rule is missing target")
+ self.0
+ .children()
+ .filter_map(RedRef::cast)
+ .nth(1)
+ .expect("show rule is missing target")
}
- /// The closure that defines the rule.
- pub fn recipe(&self) -> ClosureExpr {
- self.0.cast_last_child().expect("show rule is missing closure")
+ /// The expression that realizes the node.
+ pub fn body(&self) -> Expr {
+ self.0.cast_last_child().expect("show rule is missing body")
}
}
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index 10dfce69..9bee73ae 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -178,13 +178,21 @@ impl Category {
NodeKind::None => Some(Category::None),
NodeKind::Auto => Some(Category::Auto),
NodeKind::Ident(_) => match parent.kind() {
- NodeKind::Named => None,
- NodeKind::ClosureExpr if i == 0 => Some(Category::Function),
- NodeKind::SetExpr => Some(Category::Function),
- NodeKind::ShowExpr => Some(Category::Function),
+ NodeKind::Markup(_) => Some(Category::Variable),
NodeKind::FuncCall => Some(Category::Function),
NodeKind::MethodCall if i > 0 => Some(Category::Function),
- NodeKind::Markup(_) => Some(Category::Variable),
+ NodeKind::ClosureExpr if i == 0 => Some(Category::Function),
+ NodeKind::SetExpr => Some(Category::Function),
+ NodeKind::ShowExpr
+ if parent
+ .children()
+ .filter(|c| matches!(c.kind(), NodeKind::Ident(_)))
+ .map(RedRef::span)
+ .nth(1)
+ .map_or(false, |span| span == child.span()) =>
+ {
+ Some(Category::Function)
+ }
_ => None,
},
NodeKind::Bool(_) => Some(Category::Bool),
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 71646cb2..00bcb376 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -671,7 +671,7 @@ pub enum NodeKind {
LetExpr,
/// A set expression: `set text(...)`.
SetExpr,
- /// A show expression: `show heading(body) as [*{body}*]`.
+ /// A show expression: `show node: heading as [*{nody.body}*]`.
ShowExpr,
/// A wrap expression: `wrap body in columns(2, body)`.
WrapExpr,
diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ
index 7a5aba8f..9aabfb34 100644
--- a/tests/typ/style/show.typ
+++ b/tests/typ/style/show.typ
@@ -4,13 +4,13 @@
#let i = 1
#set heading(size: 1em)
-#show heading(level, body) as {
- if level == 1 {
+#show node: heading as {
+ if node.level == 1 {
v(10pt)
- underline(text(1.5em, blue)[{i}. #body])
+ underline(text(1.5em, blue)[{i}. {node.body}])
i += 1
} else {
- text(red, body)
+ text(red, node.body)
}
}
@@ -30,29 +30,23 @@ Another text.
---
#set heading(size: 1em, strong: false, block: false)
-#show heading(a, b) as [B]
+#show _: heading as [B]
A [= Heading] C
---
-// Error: 14-22 unexpected argument
-#show heading() as []
+// Error: 21-25 expected content, found string
+#show _: heading as "hi"
= Heading
---
-// Error: 14-28 expected content, found string
-#show heading(_, _) as "hi"
+// Error: 22-29 dictionary does not contain key: "page"
+#show it: heading as it.page
= Heading
---
-// Error: 7-12 this function cannot be customized with show
-#show upper() as {}
-
----
-// Ref: false
-// // Error: 1-29 show rule is recursive
-// #show strong(x) as strong(x)
-// *Hi*
+// Error: 10-15 this function cannot be customized with show
+#show _: upper as {}
---
// Error: 2-19 set, show and wrap are only allowed directly in markup
-{show list(a) as b}
+{show a: list as a}