Show rules with type ascribed object
This commit is contained in:
parent
7a2cc3e7d2
commit
04fb8b288a
@ -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"]);
|
||||
|
@ -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);
|
||||
|
@ -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)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,6 @@ impl Resolve for RawStroke {
|
||||
}
|
||||
}
|
||||
|
||||
// This faciliates RawStroke => Stroke.
|
||||
impl Fold for RawStroke<Length> {
|
||||
type Output = Self;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}];
|
||||
fn encode(&self) -> Dict {
|
||||
dict! {
|
||||
"url" => Value::Str(self.url.clone()),
|
||||
"body" => 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 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()));
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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 function to customize with this show rule.
|
||||
pub fn target(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("show rule is missing target")
|
||||
/// The binding to assign to.
|
||||
pub fn binding(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("show rule is missing binding")
|
||||
}
|
||||
|
||||
/// The closure that defines the rule.
|
||||
pub fn recipe(&self) -> ClosureExpr {
|
||||
self.0.cast_last_child().expect("show rule is missing closure")
|
||||
/// The function to customize with this show rule.
|
||||
pub fn target(&self) -> Ident {
|
||||
self.0
|
||||
.children()
|
||||
.filter_map(RedRef::cast)
|
||||
.nth(1)
|
||||
.expect("show rule is missing target")
|
||||
}
|
||||
|
||||
/// The expression that realizes the node.
|
||||
pub fn body(&self) -> Expr {
|
||||
self.0.cast_last_child().expect("show rule is missing body")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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}
|
||||
|
Loading…
x
Reference in New Issue
Block a user