summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ARCHITECTURE.md14
-rw-r--r--docs/src/lib.rs14
-rw-r--r--docs/src/reference/scripting.md2
-rw-r--r--docs/src/reference/types.md58
-rw-r--r--docs/src/tutorial/4-template.md7
-rw-r--r--library/src/compute/construct.rs11
-rw-r--r--library/src/compute/foundations.rs2
-rw-r--r--library/src/layout/align.rs6
-rw-r--r--library/src/layout/columns.rs16
-rw-r--r--library/src/layout/container.rs30
-rw-r--r--library/src/layout/enum.rs18
-rw-r--r--library/src/layout/flow.rs86
-rw-r--r--library/src/layout/grid.rs10
-rw-r--r--library/src/layout/hide.rs8
-rw-r--r--library/src/layout/list.rs20
-rw-r--r--library/src/layout/measure.rs2
-rw-r--r--library/src/layout/mod.rs183
-rw-r--r--library/src/layout/pad.rs6
-rw-r--r--library/src/layout/page.rs18
-rw-r--r--library/src/layout/par.rs194
-rw-r--r--library/src/layout/place.rs14
-rw-r--r--library/src/layout/regions.rs4
-rw-r--r--library/src/layout/repeat.rs10
-rw-r--r--library/src/layout/spacing.rs24
-rw-r--r--library/src/layout/stack.rs23
-rw-r--r--library/src/layout/table.rs18
-rw-r--r--library/src/layout/terms.rs28
-rw-r--r--library/src/layout/transform.rs18
-rw-r--r--library/src/lib.rs170
-rw-r--r--library/src/math/accent.rs14
-rw-r--r--library/src/math/align.rs6
-rw-r--r--library/src/math/attach.rs26
-rw-r--r--library/src/math/ctx.rs50
-rw-r--r--library/src/math/delimited.rs29
-rw-r--r--library/src/math/frac.rs16
-rw-r--r--library/src/math/fragment.rs8
-rw-r--r--library/src/math/matrix.rs22
-rw-r--r--library/src/math/mod.rs137
-rw-r--r--library/src/math/op.rs14
-rw-r--r--library/src/math/root.rs40
-rw-r--r--library/src/math/row.rs6
-rw-r--r--library/src/math/spacing.rs8
-rw-r--r--library/src/math/style.rs213
-rw-r--r--library/src/math/underover.rs36
-rw-r--r--library/src/meta/bibliography.rs106
-rw-r--r--library/src/meta/context.rs20
-rw-r--r--library/src/meta/counter.rs82
-rw-r--r--library/src/meta/document.rs24
-rw-r--r--library/src/meta/figure.rs28
-rw-r--r--library/src/meta/heading.rs45
-rw-r--r--library/src/meta/link.rs40
-rw-r--r--library/src/meta/outline.rs70
-rw-r--r--library/src/meta/query.rs28
-rw-r--r--library/src/meta/reference.rs48
-rw-r--r--library/src/meta/state.rs37
-rw-r--r--library/src/prelude.rs8
-rw-r--r--library/src/shared/behave.rs33
-rw-r--r--library/src/shared/ext.rs32
-rw-r--r--library/src/symbols/emoji.rs2
-rw-r--r--library/src/text/deco.rs30
-rw-r--r--library/src/text/misc.rs36
-rw-r--r--library/src/text/mod.rs34
-rw-r--r--library/src/text/quotes.rs4
-rw-r--r--library/src/text/raw.rs41
-rw-r--r--library/src/text/shaping.rs72
-rw-r--r--library/src/text/shift.rs42
-rw-r--r--library/src/visualize/image.rs8
-rw-r--r--library/src/visualize/line.rs8
-rw-r--r--library/src/visualize/shape.rs26
-rw-r--r--macros/src/element.rs (renamed from macros/src/node.rs)140
-rw-r--r--macros/src/lib.rs8
-rw-r--r--src/doc.rs242
-rw-r--r--src/eval/array.rs14
-rw-r--r--src/eval/func.rs69
-rw-r--r--src/eval/library.rs26
-rw-r--r--src/eval/methods.rs31
-rw-r--r--src/eval/mod.rs42
-rw-r--r--src/eval/scope.rs4
-rw-r--r--src/eval/symbol.rs126
-rw-r--r--src/eval/value.rs9
-rw-r--r--src/export/pdf/page.rs71
-rw-r--r--src/export/render.rs40
-rw-r--r--src/geom/paint.rs14
-rw-r--r--src/geom/path.rs22
-rw-r--r--src/ide/analyze.rs15
-rw-r--r--src/ide/complete.rs4
-rw-r--r--src/ide/jump.rs54
-rw-r--r--src/lib.rs4
-rw-r--r--src/model/content.rs538
-rw-r--r--src/model/element.rs145
-rw-r--r--src/model/introspect.rs170
-rw-r--r--src/model/mod.rs83
-rw-r--r--src/model/realize.rs96
-rw-r--r--src/model/styles.rs271
-rw-r--r--src/model/typeset.rs241
-rw-r--r--src/syntax/kind.rs6
-rw-r--r--src/syntax/lexer.rs8
-rw-r--r--src/syntax/mod.rs1
-rw-r--r--src/syntax/source.rs3
-rw-r--r--src/util/mod.rs25
-rw-r--r--tests/ref/compiler/show-node.pngbin24387 -> 24545 bytes
-rw-r--r--tests/ref/layout/place.pngbin44401 -> 44724 bytes
-rw-r--r--tests/src/tests.rs22
-rw-r--r--tests/typ/compiler/construct.typ2
-rw-r--r--tests/typ/compiler/content-field.typ16
-rw-r--r--tests/typ/compiler/field.typ10
-rw-r--r--tests/typ/compiler/show-node.typ12
-rw-r--r--tests/typ/compute/construct.typ4
-rw-r--r--tests/typ/layout/list-marker.typ2
-rw-r--r--tests/typ/layout/pad.typ2
-rw-r--r--tests/typ/layout/par-bidi.typ2
-rw-r--r--tests/typ/layout/place.typ4
-rw-r--r--tests/typ/meta/counter.typ2
-rw-r--r--tests/typ/meta/document.typ8
-rw-r--r--tests/typ/meta/query.typ4
-rw-r--r--tests/typ/meta/state.typ6
-rw-r--r--tests/typ/text/features.typ2
-rw-r--r--tests/typ/text/symbol.typ2
118 files changed, 2635 insertions, 2530 deletions
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 60254ba6..af48401d 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -94,10 +94,10 @@ items into a list that we want to layout, we don't realize the content within
the list items just yet. This only happens lazily once the list items are
layouted.
-When we a have realized the content into a layoutable
-node, we can then layout it into _regions,_ which describe the space into which
-the content shall be layouted. Within these, a node is free to layout itself
-as it sees fit, returning one `Frame` per region it wants to occupy.
+When we a have realized the content into a layoutable element, we can then
+layout it into _regions,_ which describe the space into which the content shall
+be layouted. Within these, an element is free to layout itself as it sees fit,
+returning one `Frame` per region it wants to occupy.
**Introspection:**
How content layouts (and realizes) may depend on how _it itself_ is layouted
@@ -108,9 +108,9 @@ introspections stabilize after one or two iterations. However, some may never
stabilize, so we give up after five attempts.
**Incremental:**
-Layout caching happens at the granularity of a node. This is important because
-overall layout is the most expensive compilation phase, so we want to reuse as
-much as possible.
+Layout caching happens at the granularity of the element. This is important
+because overall layout is the most expensive compilation phase, so we want to
+reuse as much as possible.
## Export
diff --git a/docs/src/lib.rs b/docs/src/lib.rs
index 563d565c..97535b1a 100644
--- a/docs/src/lib.rs
+++ b/docs/src/lib.rs
@@ -18,7 +18,7 @@ use typst::doc::Frame;
use typst::eval::{CastInfo, Func, FuncInfo, Library, Module, ParamInfo, Value};
use typst::font::{Font, FontBook};
use typst::geom::{Abs, Sides, Smart};
-use typst_library::layout::PageNode;
+use typst_library::layout::PageElem;
use unscanny::Scanner;
static SRC: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src");
@@ -40,9 +40,9 @@ static FONTS: Lazy<(Prehashed<FontBook>, Vec<Font>)> = Lazy::new(|| {
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
let mut lib = typst_library::build();
lib.styles
- .set(PageNode::set_width(Smart::Custom(Abs::pt(240.0).into())));
- lib.styles.set(PageNode::set_height(Smart::Auto));
- lib.styles.set(PageNode::set_margin(Sides::splat(Some(Smart::Custom(
+ .set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into())));
+ lib.styles.set(PageElem::set_height(Smart::Auto));
+ lib.styles.set(PageElem::set_margin(Sides::splat(Some(Smart::Custom(
Abs::pt(15.0).into(),
)))));
typst::eval::set_lang_items(lib.items.clone());
@@ -299,8 +299,8 @@ pub struct FuncModel {
pub name: &'static str,
pub display: &'static str,
pub oneliner: &'static str,
- pub details: Html,
pub showable: bool,
+ pub details: Html,
pub params: Vec<ParamModel>,
pub returns: Vec<&'static str>,
}
@@ -336,8 +336,8 @@ fn func_model(resolver: &dyn Resolver, func: &Func, info: &FuncInfo) -> FuncMode
name: info.name.into(),
display: info.display,
oneliner: oneliner(info.docs),
+ showable: func.element().is_some(),
details: Html::markdown(resolver, info.docs),
- showable: func.select(None).is_ok() && info.category != "math",
params: info.params.iter().map(|param| param_model(resolver, param)).collect(),
returns: info.returns.clone(),
}
@@ -632,7 +632,7 @@ fn symbol_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel {
.find(|&(_, x)| x == c)
.map(|(s, _)| s),
codepoint: c as u32,
- accent: typst::eval::combining_accent(c).is_some(),
+ accent: typst::eval::Symbol::combining_accent(c).is_some(),
unicode_name: unicode_names2::name(c)
.map(|s| s.to_string().to_title_case()),
alternates: symbol
diff --git a/docs/src/reference/scripting.md b/docs/src/reference/scripting.md
index 68e73c62..1aa399d1 100644
--- a/docs/src/reference/scripting.md
+++ b/docs/src/reference/scripting.md
@@ -138,7 +138,7 @@ For loops can iterate over a variety of collections:
- `{for value in array {..}}` \
`{for index, value in array {..}}`\
Iterates over the items in the [array]($type/array). Can also provide the
- index of each element.
+ index of each item.
- `{for value in dict {..}}` \
`{for key, value in dict {..}}` \
diff --git a/docs/src/reference/types.md b/docs/src/reference/types.md
index 355027f5..a64f4eb3 100644
--- a/docs/src/reference/types.md
+++ b/docs/src/reference/types.md
@@ -420,7 +420,7 @@ A sequence of values.
You can construct an array by enclosing a comma-separated sequence of values
in parentheses. The values do not have to be of the same type.
-You can access and update array elements with the `.at()` method. Indices are
+You can access and update array items with the `.at()` method. Indices are
zero-based and negative indices wrap around to the end of the array. You can
iterate over an array using a [for loop]($scripting/#loops).
Arrays can be added together with the `+` operator,
@@ -453,26 +453,26 @@ The number of values in the array.
- returns: integer
### first()
-Returns the first element in the array.
+Returns the first item in the array.
May be used on the left-hand side of an assignment.
Fails with an error if the array is empty.
- returns: any
### last()
-Returns the last element in the array.
+Returns the last item in the array.
May be used on the left-hand side of an assignment.
Fails with an error if the array is empty.
- returns: any
### at()
-Returns the element at the specified index in the array.
+Returns the item at the specified index in the array.
May be used on the left-hand side of an assignment.
Fails with an error if the index is out of bounds.
- index: integer (positional, required)
- The index at which to retrieve the element.
+ The index at which to retrieve the item.
- returns: any
### push()
@@ -482,7 +482,7 @@ Add a value to the end of the array.
The value to insert at the end of the array.
### pop()
-Remove the last element from the array and return it.
+Remove the last item from the array and return it.
Fails with an error if the array is empty.
- returns: any
@@ -493,7 +493,7 @@ Insert a value into the array at the specified index.
Fails with an error if the index is out of bounds.
- index: integer (positional, required)
- The index at which to insert the element.
+ The index at which to insert the item.
- value: any (positional, required)
The value to insert into the array.
@@ -501,7 +501,7 @@ Fails with an error if the index is out of bounds.
Remove the value at the specified index from the array and return it.
- index: integer (positional, required)
- The index at which to remove the element.
+ The index at which to remove the item.
- returns: any
### slice()
@@ -514,7 +514,7 @@ Fails with an error if the start or index is out of bounds.
The end index (exclusive). If omitted, the whole slice until the end of the
array is extracted.
- count: integer (named)
- The number of elements to extract. This is equivalent to passing `start +
+ The number of items to extract. This is equivalent to passing `start +
count` as the `end` position. Mutually exclusive with `end`.
- returns: array
@@ -529,59 +529,59 @@ of `{(1, 2, 3).contains(2)}`.
- returns: boolean
### find()
-Searches for an element for which the given function returns `{true}` and
+Searches for an item for which the given function returns `{true}` and
returns the first match or `{none}` if there is no match.
- searcher: function (positional, required)
- The function to apply to each element. Must return a boolean.
+ The function to apply to each item. Must return a boolean.
- returns: any or none
### position()
-Searches for an element for which the given function returns `{true}` and
+Searches for an item for which the given function returns `{true}` and
returns the index of the first match or `{none}` if there is no match.
- searcher: function (positional, required)
- The function to apply to each element. Must return a boolean.
+ The function to apply to each item. Must return a boolean.
- returns: integer or none
### filter()
-Produces a new array with only the elements from the original one for which the
+Produces a new array with only the items from the original one for which the
given function returns true.
- test: function (positional, required)
- The function to apply to each element. Must return a boolean.
+ The function to apply to each item. Must return a boolean.
- returns: array
### map()
-Produces a new array in which all elements from the original one were
+Produces a new array in which all items from the original one were
transformed with the given function.
- mapper: function (positional, required)
- The function to apply to each element.
+ The function to apply to each item.
- returns: array
### fold()
-Folds all elements into a single value using an accumulator function.
+Folds all items into a single value using an accumulator function.
- init: any (positional, required)
The initial value to start with.
- folder: function (positional, required)
The folding function. Must have two parameters: One for the accumulated value
- and one for an element.
+ and one for an item.
- returns: any
### any()
-Whether the given function returns `{true}` for any element in the array.
+Whether the given function returns `{true}` for any item in the array.
- test: function (positional, required)
- The function to apply to each element. Must return a boolean.
+ The function to apply to each item. Must return a boolean.
- returns: boolean
### all()
-Whether the given function returns `{true}` for all elements in the array.
+Whether the given function returns `{true}` for all items in the array.
- test: function (positional, required)
- The function to apply to each element. Must return a boolean.
+ The function to apply to each item. Must return a boolean.
- returns: boolean
### flatten()
@@ -590,21 +590,21 @@ Combine all nested arrays into a single flat one.
- returns: array
### rev()
-Return a new array with the same elements, but in reverse order.
+Return a new array with the same items, but in reverse order.
- returns: array
### join()
-Combine all elements in the array into one.
+Combine all items in the array into one.
- separator: any (positional)
- A value to insert between each element of the array.
+ A value to insert between each item of the array.
- last: any (named)
- An alternative separator between the last two elements
+ An alternative separator between the last two items
- returns: any
### sorted()
-Return a new array with the same elements, but sorted.
+Return a new array with the same items, but sorted.
- returns: array
@@ -658,7 +658,7 @@ present in the dictionary.
Fails with an error if the key is not part of the dictionary.
- index: integer (positional, required)
- The index at which to retrieve the element.
+ The index at which to retrieve the item.
- returns: any
### insert()
diff --git a/docs/src/tutorial/4-template.md b/docs/src/tutorial/4-template.md
index d49ee6a1..3208f012 100644
--- a/docs/src/tutorial/4-template.md
+++ b/docs/src/tutorial/4-template.md
@@ -206,11 +206,12 @@ from the dictionary, we use the [field access syntax]($scripting/#fields).
We still have to provide an argument to the grid for each author: Here is where
the array's [`map` method]($type/array.map) comes in handy. It takes a function
-as an argument that gets called with each element of the array. We pass it a
+as an argument that gets called with each item of the array. We pass it a
function that formats the details for each author and returns a new array
containing content values. We've now got one array of values that we'd like to
-use as multiple arguments for the grid. We can do that by using the
-[`spread` operator]($type/arguments). It takes an array and applies each of its elements as a separate argument to the function.
+use as multiple arguments for the grid. We can do that by using the [`spread`
+operator]($type/arguments). It takes an array and applies each of its items as a
+separate argument to the function.
The resulting template function looks like this:
diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs
index 4d6068a1..a30faf2e 100644
--- a/library/src/compute/construct.rs
+++ b/library/src/compute/construct.rs
@@ -1,7 +1,6 @@
use std::num::NonZeroI64;
use std::str::FromStr;
-use ecow::EcoVec;
use typst::eval::Regex;
use crate::prelude::*;
@@ -173,12 +172,12 @@ cast_from_value! {
Component,
v: i64 => match v {
0 ..= 255 => Self(v as u8),
- _ => Err("must be between 0 and 255")?,
+ _ => Err("number must be between 0 and 255")?,
},
v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) {
Self((v.get() * 255.0).round() as u8)
} else {
- Err("must be between 0% and 100%")?
+ Err("ratio must be between 0% and 100%")?
},
}
@@ -220,7 +219,7 @@ cast_from_value! {
v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) {
Self((v.get() * 255.0).round() as u8)
} else {
- Err("must be between 0% and 100%")?
+ Err("ratio must be between 0% and 100%")?
},
}
@@ -258,14 +257,14 @@ pub fn symbol(
#[variadic]
variants: Vec<Spanned<Variant>>,
) -> Value {
- let mut list = EcoVec::new();
+ let mut list = Vec::new();
for Spanned { v, span } in variants {
if list.iter().any(|(prev, _)| &v.0 == prev) {
bail!(span, "duplicate variant");
}
list.push((v.0, v.1));
}
- Value::Symbol(Symbol::runtime(list))
+ Value::Symbol(Symbol::runtime(list.into_boxed_slice()))
}
/// A value that can be cast to a symbol.
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index 8b148c85..d5397e60 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -134,5 +134,5 @@ pub fn eval(
source: Spanned<String>,
) -> Value {
let Spanned { v: text, span } = source;
- typst::eval::eval_code_str(vm.world(), &text, span)?
+ typst::eval::eval_string(vm.world(), &text, span)?
}
diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs
index 2a3998bf..c2f8262e 100644
--- a/library/src/layout/align.rs
+++ b/library/src/layout/align.rs
@@ -14,8 +14,8 @@ use crate::prelude::*;
///
/// Display: Align
/// Category: layout
-#[node(Show)]
-pub struct AlignNode {
+#[element(Show)]
+pub struct AlignElem {
/// The alignment along both axes.
///
/// Possible values for horizontal alignments are:
@@ -57,7 +57,7 @@ pub struct AlignNode {
pub body: Content,
}
-impl Show for AlignNode {
+impl Show for AlignElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(self
.body()
diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs
index 7704e9c4..3a1b012a 100644
--- a/library/src/layout/columns.rs
+++ b/library/src/layout/columns.rs
@@ -1,5 +1,5 @@
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
/// Separate a region into multiple equally sized columns.
///
@@ -32,8 +32,8 @@ use crate::text::TextNode;
///
/// Display: Columns
/// Category: layout
-#[node(Layout)]
-pub struct ColumnsNode {
+#[element(Layout)]
+pub struct ColumnsElem {
/// The number of columns.
#[positional]
#[default(NonZeroUsize::new(2).unwrap())]
@@ -49,7 +49,7 @@ pub struct ColumnsNode {
pub body: Content,
}
-impl Layout for ColumnsNode {
+impl Layout for ColumnsElem {
fn layout(
&self,
vt: &mut Vt,
@@ -88,7 +88,7 @@ impl Layout for ColumnsNode {
let mut frames = body.layout(vt, styles, pod)?.into_iter();
let mut finished = vec![];
- let dir = TextNode::dir_in(styles);
+ let dir = TextElem::dir_in(styles);
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
// Stitch together the columns for each region.
@@ -151,15 +151,15 @@ impl Layout for ColumnsNode {
///
/// Display: Column Break
/// Category: layout
-#[node(Behave)]
-pub struct ColbreakNode {
+#[element(Behave)]
+pub struct ColbreakElem {
/// If `{true}`, the column break is skipped if the current column is
/// already empty.
#[default(false)]
pub weak: bool,
}
-impl Behave for ColbreakNode {
+impl Behave for ColbreakElem {
fn behaviour(&self) -> Behaviour {
if self.weak(StyleChain::default()) {
Behaviour::Weak(1)
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index bdc147c3..ef7def7a 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -1,4 +1,4 @@
-use super::VNode;
+use super::VElem;
use crate::layout::Spacing;
use crate::prelude::*;
@@ -21,8 +21,8 @@ use crate::prelude::*;
///
/// Display: Box
/// Category: layout
-#[node(Layout)]
-pub struct BoxNode {
+#[element(Layout)]
+pub struct BoxElem {
/// The width of the box.
///
/// Boxes can have [fractional]($type/fraction) widths, as the example
@@ -93,7 +93,7 @@ pub struct BoxNode {
pub body: Option<Content>,
}
-impl Layout for BoxNode {
+impl Layout for BoxElem {
fn layout(
&self,
vt: &mut Vt,
@@ -183,8 +183,8 @@ impl Layout for BoxNode {
///
/// Display: Block
/// Category: layout
-#[node(Layout)]
-pub struct BlockNode {
+#[element(Layout)]
+pub struct BlockElem {
/// The block's width.
///
/// ```example
@@ -278,11 +278,11 @@ pub struct BlockNode {
#[parse(
let spacing = args.named("spacing")?;
args.named("above")?
- .map(VNode::block_around)
- .or_else(|| spacing.map(VNode::block_spacing))
+ .map(VElem::block_around)
+ .or_else(|| spacing.map(VElem::block_spacing))
)]
- #[default(VNode::block_spacing(Em::new(1.2).into()))]
- pub above: VNode,
+ #[default(VElem::block_spacing(Em::new(1.2).into()))]
+ pub above: VElem,
/// The spacing between this block and its successor. Takes precedence
/// over `spacing`.
@@ -290,11 +290,11 @@ pub struct BlockNode {
/// The default value is `{1.2em}`.
#[parse(
args.named("below")?
- .map(VNode::block_around)
- .or_else(|| spacing.map(VNode::block_spacing))
+ .map(VElem::block_around)
+ .or_else(|| spacing.map(VElem::block_spacing))
)]
- #[default(VNode::block_spacing(Em::new(1.2).into()))]
- pub below: VNode,
+ #[default(VElem::block_spacing(Em::new(1.2).into()))]
+ pub below: VElem,
/// The contents of the block.
#[positional]
@@ -308,7 +308,7 @@ pub struct BlockNode {
pub sticky: bool,
}
-impl Layout for BlockNode {
+impl Layout for BlockElem {
fn layout(
&self,
vt: &mut Vt,
diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs
index 05b42bd8..1be57d4c 100644
--- a/library/src/layout/enum.rs
+++ b/library/src/layout/enum.rs
@@ -1,9 +1,9 @@
use std::str::FromStr;
-use crate::layout::{BlockNode, ParNode, Sizing, Spacing};
+use crate::layout::{BlockElem, ParElem, Sizing, Spacing};
use crate::meta::{Numbering, NumberingPattern};
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
use super::GridLayouter;
@@ -50,8 +50,8 @@ use super::GridLayouter;
///
/// Display: Numbered List
/// Category: layout
-#[node(Layout)]
-pub struct EnumNode {
+#[element(Layout)]
+pub struct EnumElem {
/// If this is `{false}`, the items are spaced apart with
/// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal
/// [leading]($func/par.leading) instead. This makes the enumeration more
@@ -153,7 +153,7 @@ pub struct EnumNode {
parents: Parent,
}
-impl Layout for EnumNode {
+impl Layout for EnumElem {
fn layout(
&self,
vt: &mut Vt,
@@ -164,10 +164,10 @@ impl Layout for EnumNode {
let indent = self.indent(styles);
let body_indent = self.body_indent(styles);
let gutter = if self.tight(styles) {
- ParNode::leading_in(styles).into()
+ ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockNode::below_in(styles).amount())
+ .unwrap_or_else(|| BlockElem::below_in(styles).amount())
};
let mut cells = vec![];
@@ -186,7 +186,7 @@ impl Layout for EnumNode {
} else {
match &numbering {
Numbering::Pattern(pattern) => {
- TextNode::packed(pattern.apply_kth(parents.len(), number))
+ TextElem::packed(pattern.apply_kth(parents.len(), number))
}
other => other.apply_vt(vt, &[number])?.display(),
}
@@ -221,7 +221,7 @@ impl Layout for EnumNode {
///
/// Display: Numbered List Item
/// Category: layout
-#[node]
+#[element]
pub struct EnumItem {
/// The item's number.
#[positional]
diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs
index 096c575e..b6476816 100644
--- a/library/src/layout/flow.rs
+++ b/library/src/layout/flow.rs
@@ -1,24 +1,22 @@
-use typst::model::StyledNode;
-
-use super::{AlignNode, BlockNode, ColbreakNode, ParNode, PlaceNode, Spacing, VNode};
+use super::{AlignElem, BlockElem, ColbreakElem, ParElem, PlaceElem, Spacing, VElem};
use crate::prelude::*;
-use crate::visualize::{CircleNode, EllipseNode, ImageNode, RectNode, SquareNode};
+use crate::visualize::{CircleElem, EllipseElem, ImageElem, RectElem, SquareElem};
-/// Arrange spacing, paragraphs and block-level nodes into a flow.
+/// Arrange spacing, paragraphs and block-level elements into a flow.
///
-/// This node is responsible for layouting both the top-level content flow and
+/// This element is responsible for layouting both the top-level content flow and
/// the contents of boxes.
///
/// Display: Flow
/// Category: layout
-#[node(Layout)]
-pub struct FlowNode {
+#[element(Layout)]
+pub struct FlowElem {
/// The children that will be arranges into a flow.
#[variadic]
pub children: Vec<Content>,
}
-impl Layout for FlowNode {
+impl Layout for FlowElem {
fn layout(
&self,
vt: &mut Vt,
@@ -27,29 +25,27 @@ impl Layout for FlowNode {
) -> SourceResult<Fragment> {
let mut layouter = FlowLayouter::new(regions);
- for mut child in self.children() {
- let map;
+ for mut child in &self.children() {
let outer = styles;
- let mut styles = outer;
- if let Some(node) = child.to::<StyledNode>() {
- map = node.styles();
+ let mut styles = styles;
+ if let Some((elem, map)) = child.to_styled() {
+ child = elem;
styles = outer.chain(&map);
- child = node.body();
}
- if let Some(node) = child.to::<VNode>() {
- layouter.layout_spacing(node, styles);
- } else if let Some(node) = child.to::<ParNode>() {
- layouter.layout_par(vt, node, styles)?;
- } else if child.is::<RectNode>()
- || child.is::<SquareNode>()
- || child.is::<EllipseNode>()
- || child.is::<CircleNode>()
- || child.is::<ImageNode>()
+ if let Some(elem) = child.to::<VElem>() {
+ layouter.layout_spacing(elem, styles);
+ } else if let Some(elem) = child.to::<ParElem>() {
+ layouter.layout_par(vt, elem, styles)?;
+ } else if child.is::<RectElem>()
+ || child.is::<SquareElem>()
+ || child.is::<EllipseElem>()
+ || child.is::<CircleElem>()
+ || child.is::<ImageElem>()
{
let layoutable = child.with::<dyn Layout>().unwrap();
layouter.layout_single(vt, layoutable, styles)?;
- } else if child.is::<MetaNode>() {
+ } else if child.is::<MetaElem>() {
let mut frame = Frame::new(Size::zero());
frame.meta(styles, true);
layouter.items.push(FlowItem::Frame(
@@ -59,7 +55,7 @@ impl Layout for FlowNode {
));
} else if child.can::<dyn Layout>() {
layouter.layout_multiple(vt, &child, styles)?;
- } else if child.is::<ColbreakNode>() {
+ } else if child.is::<ColbreakElem>() {
if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some()
{
layouter.finish_region();
@@ -122,13 +118,13 @@ impl<'a> FlowLayouter<'a> {
}
/// Layout vertical spacing.
- fn layout_spacing(&mut self, node: &VNode, styles: StyleChain) {
- self.layout_item(match node.amount() {
- Spacing::Rel(v) => FlowItem::Absolute(
- v.resolve(styles).relative_to(self.initial.y),
- node.weakness(styles) > 0,
+ fn layout_spacing(&mut self, v: &VElem, styles: StyleChain) {
+ self.layout_item(match v.amount() {
+ Spacing::Rel(rel) => FlowItem::Absolute(
+ rel.resolve(styles).relative_to(self.initial.y),
+ v.weakness(styles) > 0,
),
- Spacing::Fr(v) => FlowItem::Fractional(v),
+ Spacing::Fr(fr) => FlowItem::Fractional(fr),
});
}
@@ -136,11 +132,11 @@ impl<'a> FlowLayouter<'a> {
fn layout_par(
&mut self,
vt: &mut Vt,
- par: &ParNode,
+ par: &ParElem,
styles: StyleChain,
) -> SourceResult<()> {
- let aligns = AlignNode::alignment_in(styles).resolve(styles);
- let leading = ParNode::leading_in(styles);
+ let aligns = AlignElem::alignment_in(styles).resolve(styles);
+ let leading = ParElem::leading_in(styles);
let consecutive = self.last_was_par;
let frames = par
.layout(vt, styles, consecutive, self.regions.base(), self.regions.expand.x)?
@@ -185,8 +181,8 @@ impl<'a> FlowLayouter<'a> {
content: &dyn Layout,
styles: StyleChain,
) -> SourceResult<()> {
- let aligns = AlignNode::alignment_in(styles).resolve(styles);
- let sticky = BlockNode::sticky_in(styles);
+ let aligns = AlignElem::alignment_in(styles).resolve(styles);
+ let sticky = BlockElem::sticky_in(styles);
let pod = Regions::one(self.regions.base(), Axes::splat(false));
let frame = content.layout(vt, styles, pod)?.into_frame();
self.layout_item(FlowItem::Frame(frame, aligns, sticky));
@@ -201,9 +197,9 @@ impl<'a> FlowLayouter<'a> {
block: &Content,
styles: StyleChain,
) -> SourceResult<()> {
- // Placed nodes that are out of flow produce placed items which aren't
- // aligned later.
- if let Some(placed) = block.to::<PlaceNode>() {
+ // Placed elements that are out of flow produce placed items which
+ // aren't aligned later.
+ if let Some(placed) = block.to::<PlaceElem>() {
if placed.out_of_flow(styles) {
let frame = block.layout(vt, styles, self.regions)?.into_frame();
self.layout_item(FlowItem::Placed(frame));
@@ -212,17 +208,17 @@ impl<'a> FlowLayouter<'a> {
}
// How to align the block.
- let aligns = if let Some(align) = block.to::<AlignNode>() {
+ let aligns = if let Some(align) = block.to::<AlignElem>() {
align.alignment(styles)
- } else if let Some(styled) = block.to::<StyledNode>() {
- AlignNode::alignment_in(styles.chain(&styled.styles()))
+ } else if let Some((_, local)) = block.to_styled() {
+ AlignElem::alignment_in(styles.chain(local))
} else {
- AlignNode::alignment_in(styles)
+ AlignElem::alignment_in(styles)
}
.resolve(styles);
// Layout the block itself.
- let sticky = BlockNode::sticky_in(styles);
+ let sticky = BlockElem::sticky_in(styles);
let fragment = block.layout(vt, styles, self.regions)?;
for (i, frame) in fragment.into_iter().enumerate() {
if i > 0 {
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index 47d3ab86..5c3d132e 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -1,5 +1,5 @@
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
use super::Sizing;
@@ -61,8 +61,8 @@ use super::Sizing;
///
/// Display: Grid
/// Category: layout
-#[node(Layout)]
-pub struct GridNode {
+#[element(Layout)]
+pub struct GridElem {
/// Defines the column sizes.
///
/// Either specify a track size array or provide an integer to create a grid
@@ -101,7 +101,7 @@ pub struct GridNode {
pub children: Vec<Content>,
}
-impl Layout for GridNode {
+impl Layout for GridElem {
fn layout(
&self,
vt: &mut Vt,
@@ -257,7 +257,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
// Reverse for RTL.
- let is_rtl = TextNode::dir_in(styles) == Dir::RTL;
+ let is_rtl = TextElem::dir_in(styles) == Dir::RTL;
if is_rtl {
cols.reverse();
}
diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs
index 1d87d3e8..d9bee317 100644
--- a/library/src/layout/hide.rs
+++ b/library/src/layout/hide.rs
@@ -15,15 +15,15 @@ use crate::prelude::*;
///
/// Display: Hide
/// Category: layout
-#[node(Show)]
-pub struct HideNode {
+#[element(Show)]
+pub struct HideElem {
/// The content to hide.
#[required]
pub body: Content,
}
-impl Show for HideNode {
+impl Show for HideElem {
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(MetaNode::set_data(vec![Meta::Hide])))
+ Ok(self.body().styled(MetaElem::set_data(vec![Meta::Hide])))
}
}
diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs
index fe78131d..179c93eb 100644
--- a/library/src/layout/list.rs
+++ b/library/src/layout/list.rs
@@ -1,6 +1,6 @@
-use crate::layout::{BlockNode, ParNode, Sizing, Spacing};
+use crate::layout::{BlockElem, ParElem, Sizing, Spacing};
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
use super::GridLayouter;
@@ -36,8 +36,8 @@ use super::GridLayouter;
///
/// Display: Bullet List
/// Category: layout
-#[node(Layout)]
-pub struct ListNode {
+#[element(Layout)]
+pub struct ListElem {
/// If this is `{false}`, the items are spaced apart with [list
/// spacing]($func/list.spacing). If it is `{true}`, they use normal
/// [leading]($func/par.leading) instead. This makes the list more compact,
@@ -111,7 +111,7 @@ pub struct ListNode {
depth: Depth,
}
-impl Layout for ListNode {
+impl Layout for ListElem {
fn layout(
&self,
vt: &mut Vt,
@@ -121,10 +121,10 @@ impl Layout for ListNode {
let indent = self.indent(styles);
let body_indent = self.body_indent(styles);
let gutter = if self.tight(styles) {
- ParNode::leading_in(styles).into()
+ ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockNode::below_in(styles).amount())
+ .unwrap_or_else(|| BlockElem::below_in(styles).amount())
};
let depth = self.depth(styles);
@@ -160,7 +160,7 @@ impl Layout for ListNode {
///
/// Display: Bullet List Item
/// Category: layout
-#[node]
+#[element]
pub struct ListItem {
/// The item's body.
#[required]
@@ -187,7 +187,7 @@ impl ListMarker {
.get(depth)
.or(list.last())
.cloned()
- .unwrap_or_else(|| TextNode::packed('•')),
+ .unwrap_or_else(|| TextElem::packed('•')),
Self::Func(func) => func.call_vt(vt, [Value::Int(depth as i64)])?.display(),
})
}
@@ -198,7 +198,7 @@ cast_from_value! {
v: Content => Self::Content(vec![v]),
array: Array => {
if array.len() == 0 {
- Err("must contain at least one marker")?;
+ Err("array must contain at least one marker")?;
}
Self::Content(array.into_iter().map(Value::display).collect())
},
diff --git a/library/src/layout/measure.rs b/library/src/layout/measure.rs
index b116cbf8..df66d67f 100644
--- a/library/src/layout/measure.rs
+++ b/library/src/layout/measure.rs
@@ -10,7 +10,7 @@ pub fn measure(
/// The content whose size to measure.
content: Content,
/// The styles with which to layout the content.
- styles: StyleMap,
+ styles: Styles,
) -> Value {
let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
let styles = StyleChain::new(&styles);
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index b6ecce51..02d3bca5 100644
--- a/library/src/layout/mod.rs
+++ b/library/src/layout/mod.rs
@@ -50,14 +50,14 @@ use std::mem;
use typed_arena::Arena;
use typst::diag::SourceResult;
use typst::eval::Tracer;
-use typst::model::{applicable, realize, SequenceNode, StyleVecBuilder, StyledNode};
+use typst::model::{applicable, realize, StyleVecBuilder};
-use crate::math::{EquationNode, LayoutMath};
-use crate::meta::DocumentNode;
+use crate::math::{EquationElem, LayoutMath};
+use crate::meta::DocumentElem;
use crate::prelude::*;
use crate::shared::BehavedBuilder;
-use crate::text::{LinebreakNode, SmartQuoteNode, SpaceNode, TextNode};
-use crate::visualize::{CircleNode, EllipseNode, ImageNode, RectNode, SquareNode};
+use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
+use crate::visualize::{CircleElem, EllipseElem, ImageElem, RectElem, SquareElem};
/// Root-level layout.
pub trait LayoutRoot {
@@ -69,7 +69,7 @@ impl LayoutRoot for Content {
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
#[comemo::memoize]
fn cached(
- node: &Content,
+ content: &Content,
world: Tracked<dyn World>,
tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>,
@@ -78,7 +78,7 @@ impl LayoutRoot for Content {
) -> SourceResult<Document> {
let mut vt = Vt { world, tracer, provider, introspector };
let scratch = Scratch::default();
- let (realized, styles) = realize_root(&mut vt, &scratch, node, styles)?;
+ let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?;
realized
.with::<dyn LayoutRoot>()
.unwrap()
@@ -108,8 +108,8 @@ pub trait Layout {
/// Layout without side effects.
///
- /// This node must be layouted again in the same order for the results to be
- /// valid.
+ /// This element must be layouted again in the same order for the results to
+ /// be valid.
fn measure(
&self,
vt: &mut Vt,
@@ -132,7 +132,7 @@ impl Layout for Content {
) -> SourceResult<Fragment> {
#[comemo::memoize]
fn cached(
- node: &Content,
+ content: &Content,
world: Tracked<dyn World>,
tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>,
@@ -142,7 +142,7 @@ impl Layout for Content {
) -> SourceResult<Fragment> {
let mut vt = Vt { world, tracer, provider, introspector };
let scratch = Scratch::default();
- let (realized, styles) = realize_block(&mut vt, &scratch, node, styles)?;
+ let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?;
realized
.with::<dyn Layout>()
.unwrap()
@@ -161,7 +161,7 @@ impl Layout for Content {
}
}
-/// Realize into a node that is capable of root-level layout.
+/// Realize into an element that is capable of root-level layout.
fn realize_root<'a>(
vt: &mut Vt,
scratch: &'a Scratch<'a>,
@@ -176,10 +176,10 @@ fn realize_root<'a>(
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles))?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
- Ok((DocumentNode::new(pages.to_vec()).pack(), shared))
+ Ok((DocumentElem::new(pages.to_vec()).pack(), shared))
}
-/// Realize into a node that is capable of block-level layout.
+/// Realize into an element that is capable of block-level layout.
fn realize_block<'a>(
vt: &mut Vt,
scratch: &'a Scratch<'a>,
@@ -187,11 +187,11 @@ fn realize_block<'a>(
styles: StyleChain<'a>,
) -> SourceResult<(Content, StyleChain<'a>)> {
if content.can::<dyn Layout>()
- && !content.is::<RectNode>()
- && !content.is::<SquareNode>()
- && !content.is::<EllipseNode>()
- && !content.is::<CircleNode>()
- && !content.is::<ImageNode>()
+ && !content.is::<RectElem>()
+ && !content.is::<SquareElem>()
+ && !content.is::<EllipseElem>()
+ && !content.is::<CircleElem>()
+ && !content.is::<ImageElem>()
&& !applicable(content, styles)
{
return Ok((content.clone(), styles));
@@ -201,10 +201,10 @@ fn realize_block<'a>(
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (children, shared) = builder.flow.0.finish();
- Ok((FlowNode::new(children.to_vec()).pack(), shared))
+ Ok((FlowElem::new(children.to_vec()).pack(), shared))
}
-/// Builds a document or a flow node from content.
+/// Builds a document or a flow element from content.
struct Builder<'a, 'v, 't> {
/// The virtual typesetter.
vt: &'v mut Vt<'t>,
@@ -227,7 +227,6 @@ struct Scratch<'a> {
styles: Arena<StyleChain<'a>>,
/// An arena where intermediate content resulting from show rules is stored.
content: Arena<Content>,
- maps: Arena<StyleMap>,
}
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
@@ -247,19 +246,18 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
mut content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<()> {
- if content.can::<dyn LayoutMath>() && !content.is::<EquationNode>() {
+ if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() {
content =
- self.scratch.content.alloc(EquationNode::new(content.clone()).pack());
+ self.scratch.content.alloc(EquationElem::new(content.clone()).pack());
}
- if let Some(styled) = content.to::<StyledNode>() {
- return self.styled(styled, styles);
+ if let Some((elem, local)) = content.to_styled() {
+ return self.styled(elem, local, styles);
}
- if let Some(seq) = content.to::<SequenceNode>() {
- for sub in seq.children() {
- let stored = self.scratch.content.alloc(sub);
- self.accept(stored, styles)?;
+ if let Some(children) = content.to_sequence() {
+ for elem in children {
+ self.accept(elem, styles)?;
}
return Ok(());
}
@@ -290,7 +288,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
}
let keep = content
- .to::<PagebreakNode>()
+ .to::<PagebreakElem>()
.map_or(false, |pagebreak| !pagebreak.weak(styles));
self.interrupt_page(keep.then(|| styles))?;
@@ -301,52 +299,55 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
}
}
- bail!(content.span(), "not allowed here");
+ if content.is::<PagebreakElem>() {
+ bail!(content.span(), "pagebreaks are not allowed inside of containers");
+ } else {
+ bail!(content.span(), "{} is not allowed here", content.func().name());
+ }
}
fn styled(
&mut self,
- styled: &'a StyledNode,
+ elem: &'a Content,
+ map: &'a Styles,
styles: StyleChain<'a>,
) -> SourceResult<()> {
- let map = self.scratch.maps.alloc(styled.styles());
let stored = self.scratch.styles.alloc(styles);
- let content = self.scratch.content.alloc(styled.body());
let styles = stored.chain(map);
self.interrupt_style(&map, None)?;
- self.accept(content, styles)?;
+ self.accept(elem, styles)?;
self.interrupt_style(map, Some(styles))?;
Ok(())
}
fn interrupt_style(
&mut self,
- map: &StyleMap,
- styles: Option<StyleChain<'a>>,
+ local: &Styles,
+ outer: Option<StyleChain<'a>>,
) -> SourceResult<()> {
- if let Some(Some(span)) = map.interruption::<DocumentNode>() {
+ if let Some(Some(span)) = local.interruption::<DocumentElem>() {
if self.doc.is_none() {
- bail!(span, "not allowed here");
+ bail!(span, "document set rules are not allowed inside of containers");
}
- if styles.is_none()
+ if outer.is_none()
&& (!self.flow.0.is_empty()
|| !self.par.0.is_empty()
|| !self.list.items.is_empty())
{
- bail!(span, "must appear before any content");
+ bail!(span, "document set rules must appear before any content");
}
- } else if let Some(Some(span)) = map.interruption::<PageNode>() {
+ } else if let Some(Some(span)) = local.interruption::<PageElem>() {
if self.doc.is_none() {
- bail!(span, "not allowed here");
+ bail!(span, "page configuration is not allowed inside of containers");
}
- self.interrupt_page(styles)?;
- } else if map.interruption::<ParNode>().is_some()
- || map.interruption::<AlignNode>().is_some()
+ self.interrupt_page(outer)?;
+ } else if local.interruption::<ParElem>().is_some()
+ || local.interruption::<AlignElem>().is_some()
{
self.interrupt_par()?;
- } else if map.interruption::<ListNode>().is_some()
- || map.interruption::<EnumNode>().is_some()
- || map.interruption::<TermsNode>().is_some()
+ } else if local.interruption::<ListElem>().is_some()
+ || local.interruption::<EnumElem>().is_some()
+ || local.interruption::<TermsElem>().is_some()
{
self.interrupt_list()?;
}
@@ -387,7 +388,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
} else {
shared
};
- let page = PageNode::new(FlowNode::new(flow.to_vec()).pack()).pack();
+ let page = PageElem::new(FlowElem::new(flow.to_vec()).pack()).pack();
let stored = self.scratch.content.alloc(page);
self.accept(stored, styles)?;
}
@@ -405,12 +406,12 @@ struct DocBuilder<'a> {
impl<'a> DocBuilder<'a> {
fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool {
- if let Some(pagebreak) = content.to::<PagebreakNode>() {
+ if let Some(pagebreak) = content.to::<PagebreakElem>() {
self.keep_next = !pagebreak.weak(styles);
return true;
}
- if content.is::<PageNode>() {
+ if content.is::<PageElem>() {
self.pages.push(content.clone(), styles);
self.keep_next = false;
return true;
@@ -432,7 +433,7 @@ struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
impl<'a> FlowBuilder<'a> {
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
- if content.is::<ParbreakNode>() {
+ if content.is::<ParbreakElem>() {
self.1 = true;
return true;
}
@@ -440,33 +441,33 @@ impl<'a> FlowBuilder<'a> {
let last_was_parbreak = self.1;
self.1 = false;
- if content.is::<VNode>()
- || content.is::<ColbreakNode>()
- || content.is::<MetaNode>()
+ if content.is::<VElem>()
+ || content.is::<ColbreakElem>()
+ || content.is::<MetaElem>()
{
self.0.push(content.clone(), styles);
return true;
}
- if content.can::<dyn Layout>() || content.is::<ParNode>() {
- let is_tight_list = if let Some(node) = content.to::<ListNode>() {
- node.tight(styles)
- } else if let Some(node) = content.to::<EnumNode>() {
- node.tight(styles)
- } else if let Some(node) = content.to::<TermsNode>() {
- node.tight(styles)
+ if content.can::<dyn Layout>() || content.is::<ParElem>() {
+ let is_tight_list = if let Some(elem) = content.to::<ListElem>() {
+ elem.tight(styles)
+ } else if let Some(elem) = content.to::<EnumElem>() {
+ elem.tight(styles)
+ } else if let Some(elem) = content.to::<TermsElem>() {
+ elem.tight(styles)
} else {
false
};
if !last_was_parbreak && is_tight_list {
- let leading = ParNode::leading_in(styles);
- let spacing = VNode::list_attach(leading.into());
+ let leading = ParElem::leading_in(styles);
+ let spacing = VElem::list_attach(leading.into());
self.0.push(spacing.pack(), styles);
}
- let above = BlockNode::above_in(styles);
- let below = BlockNode::below_in(styles);
+ let above = BlockElem::above_in(styles);
+ let below = BlockElem::below_in(styles);
self.0.push(above.clone().pack(), styles);
self.0.push(content.clone(), styles);
self.0.push(below.clone().pack(), styles);
@@ -483,18 +484,18 @@ struct ParBuilder<'a>(BehavedBuilder<'a>);
impl<'a> ParBuilder<'a> {
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
- if content.is::<MetaNode>() {
+ if content.is::<MetaElem>() {
if !self.0.is_basically_empty() {
self.0.push(content.clone(), styles);
return true;
}
- } else if content.is::<SpaceNode>()
- || content.is::<TextNode>()
- || content.is::<HNode>()
- || content.is::<LinebreakNode>()
- || content.is::<SmartQuoteNode>()
- || content.to::<EquationNode>().map_or(false, |node| !node.block(styles))
- || content.is::<BoxNode>()
+ } else if content.is::<SpaceElem>()
+ || content.is::<TextElem>()
+ || content.is::<HElem>()
+ || content.is::<LinebreakElem>()
+ || content.is::<SmartQuoteElem>()
+ || content.to::<EquationElem>().map_or(false, |elem| !elem.block(styles))
+ || content.is::<BoxElem>()
{
self.0.push(content.clone(), styles);
return true;
@@ -505,7 +506,7 @@ impl<'a> ParBuilder<'a> {
fn finish(self) -> (Content, StyleChain<'a>) {
let (children, shared) = self.0.finish();
- (ParNode::new(children.to_vec()).pack(), shared)
+ (ParElem::new(children.to_vec()).pack(), shared)
}
}
@@ -522,7 +523,7 @@ struct ListBuilder<'a> {
impl<'a> ListBuilder<'a> {
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
if !self.items.is_empty()
- && (content.is::<SpaceNode>() || content.is::<ParbreakNode>())
+ && (content.is::<SpaceElem>() || content.is::<ParbreakElem>())
{
self.staged.push((content, styles));
return true;
@@ -533,12 +534,12 @@ impl<'a> ListBuilder<'a> {
|| content.is::<TermItem>())
&& self
.items
- .items()
+ .elems()
.next()
- .map_or(true, |first| first.id() == content.id())
+ .map_or(true, |first| first.func() == content.func())
{
self.items.push(content.clone(), styles);
- self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakNode>());
+ self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>());
return true;
}
@@ -549,39 +550,39 @@ impl<'a> ListBuilder<'a> {
let (items, shared) = self.items.finish();
let item = items.items().next().unwrap();
let output = if item.is::<ListItem>() {
- ListNode::new(
+ ListElem::new(
items
.iter()
- .map(|(item, map)| {
+ .map(|(item, local)| {
let item = item.to::<ListItem>().unwrap();
- item.clone().with_body(item.body().styled_with_map(map.clone()))
+ item.clone().with_body(item.body().styled_with_map(local.clone()))
})
.collect::<Vec<_>>(),
)
.with_tight(self.tight)
.pack()
} else if item.is::<EnumItem>() {
- EnumNode::new(
+ EnumElem::new(
items
.iter()
- .map(|(item, map)| {
+ .map(|(item, local)| {
let item = item.to::<EnumItem>().unwrap();
- item.clone().with_body(item.body().styled_with_map(map.clone()))
+ item.clone().with_body(item.body().styled_with_map(local.clone()))
})
.collect::<Vec<_>>(),
)
.with_tight(self.tight)
.pack()
} else if item.is::<TermItem>() {
- TermsNode::new(
+ TermsElem::new(
items
.iter()
- .map(|(item, map)| {
+ .map(|(item, local)| {
let item = item.to::<TermItem>().unwrap();
item.clone()
- .with_term(item.term().styled_with_map(map.clone()))
+ .with_term(item.term().styled_with_map(local.clone()))
.with_description(
- item.description().styled_with_map(map.clone()),
+ item.description().styled_with_map(local.clone()),
)
})
.collect::<Vec<_>>(),
diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs
index e8171560..441aa211 100644
--- a/library/src/layout/pad.rs
+++ b/library/src/layout/pad.rs
@@ -17,8 +17,8 @@ use crate::prelude::*;
///
/// Display: Padding
/// Category: layout
-#[node(Layout)]
-pub struct PadNode {
+#[element(Layout)]
+pub struct PadElem {
/// The padding at the left side.
#[parse(
let all = args.named("rest")?.or(args.find()?);
@@ -59,7 +59,7 @@ pub struct PadNode {
pub body: Content,
}
-impl Layout for PadNode {
+impl Layout for PadElem {
fn layout(
&self,
vt: &mut Vt,
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index 93ee08ce..a8a806ad 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -1,7 +1,7 @@
use std::ptr;
use std::str::FromStr;
-use super::{AlignNode, ColumnsNode};
+use super::{AlignElem, ColumnsElem};
use crate::meta::{Counter, CounterKey, Numbering};
use crate::prelude::*;
@@ -24,8 +24,8 @@ use crate::prelude::*;
///
/// Display: Page
/// Category: layout
-#[node]
-pub struct PageNode {
+#[element]
+pub struct PageElem {
/// A standard paper size to set width and height. When this is not
/// specified, Typst defaults to `{"a4"}` paper.
#[external]
@@ -270,7 +270,7 @@ pub struct PageNode {
pub body: Content,
}
-impl PageNode {
+impl PageElem {
/// Layout the page run into a sequence of frames, one per page.
pub fn layout(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Fragment> {
// When one of the lengths is infinite the page fits its content along
@@ -296,7 +296,7 @@ impl PageNode {
// Realize columns.
let columns = self.columns(styles);
if columns.get() > 1 {
- child = ColumnsNode::new(child).with_count(columns).pack();
+ child = ColumnsElem::new(child).with_count(columns).pack();
}
// Realize margins.
@@ -356,7 +356,7 @@ impl PageNode {
let pod = Regions::one(area, Axes::splat(true));
let sub = content
.clone()
- .styled(AlignNode::set_alignment(align))
+ .styled(AlignElem::set_alignment(align))
.layout(vt, styles, pod)?
.into_frame();
if ptr::eq(marginal, &header) || ptr::eq(marginal, &background) {
@@ -387,8 +387,8 @@ impl PageNode {
///
/// Display: Page Break
/// Category: layout
-#[node]
-pub struct PagebreakNode {
+#[element]
+pub struct PagebreakElem {
/// If `{true}`, the page break is skipped if the current page is already
/// empty.
#[default(false)]
@@ -467,7 +467,7 @@ macro_rules! papers {
fn from_str(name: &str) -> Result<Self, Self::Err> {
match name.to_lowercase().as_str() {
$($pat => Ok(Self::$var),)*
- _ => Err("invalid paper name"),
+ _ => Err("unknown paper size"),
}
}
}
diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs
index cef0d11c..db65b125 100644
--- a/library/src/layout/par.rs
+++ b/library/src/layout/par.rs
@@ -3,17 +3,15 @@ use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript};
use xi_unicode::LineBreakIterator;
-use typst::model::StyledNode;
-
-use super::{BoxNode, HNode, Sizing, Spacing};
-use crate::layout::AlignNode;
-use crate::math::EquationNode;
+use super::{BoxElem, HElem, Sizing, Spacing};
+use crate::layout::AlignElem;
+use crate::math::EquationElem;
use crate::prelude::*;
use crate::text::{
- shape, LinebreakNode, Quoter, Quotes, ShapedText, SmartQuoteNode, SpaceNode, TextNode,
+ shape, LinebreakElem, Quoter, Quotes, ShapedText, SmartQuoteElem, SpaceElem, TextElem,
};
-/// Arrange text, spacing and inline-level nodes into a paragraph.
+/// Arrange text, spacing and inline-level elements into a paragraph.
///
/// Although this function is primarily used in set rules to affect paragraph
/// properties, it can also be used to explicitly render its argument onto a
@@ -38,8 +36,8 @@ use crate::text::{
///
/// Display: Paragraph
/// Category: layout
-#[node(Construct)]
-pub struct ParNode {
+#[element(Construct)]
+pub struct ParElem {
/// The spacing between lines.
///
/// The default value is `{0.65em}`.
@@ -110,22 +108,22 @@ pub struct ParNode {
pub children: Vec<Content>,
}
-impl Construct for ParNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+impl Construct for ParElem {
+ fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
// The paragraph constructor is special: It doesn't create a paragraph
- // node. Instead, it just ensures that the passed content lives in a
+ // element. Instead, it just ensures that the passed content lives in a
// separate paragraph and styles it.
let styles = Self::set(args)?;
let body = args.expect::<Content>("body")?;
- Ok(Content::sequence(vec![
- ParbreakNode::new().pack(),
+ Ok(Content::sequence([
+ ParbreakElem::new().pack(),
body.styled_with_map(styles),
- ParbreakNode::new().pack(),
+ ParbreakElem::new().pack(),
]))
}
}
-impl ParNode {
+impl ParElem {
/// Layout the paragraph into a collection of lines.
pub fn layout(
&self,
@@ -137,7 +135,7 @@ impl ParNode {
) -> SourceResult<Fragment> {
#[comemo::memoize]
fn cached(
- par: &ParNode,
+ par: &ParElem,
world: Tracked<dyn World>,
tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>,
@@ -179,26 +177,6 @@ impl ParNode {
}
}
-/// A horizontal alignment.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct HorizontalAlign(pub GenAlign);
-
-cast_from_value! {
- HorizontalAlign,
- align: GenAlign => match align.axis() {
- Axis::X => Self(align),
- Axis::Y => Err("must be horizontal")?,
- },
-}
-
-impl Resolve for HorizontalAlign {
- type Output = Align;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.0.resolve(styles)
- }
-}
-
/// How to determine line breaks in a paragraph.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum Linebreaks {
@@ -232,10 +210,10 @@ pub enum Linebreaks {
///
/// Display: Paragraph Break
/// Category: layout
-#[node(Unlabellable)]
-pub struct ParbreakNode {}
+#[element(Unlabellable)]
+pub struct ParbreakElem {}
-impl Unlabellable for ParbreakNode {}
+impl Unlabellable for ParbreakElem {}
/// Range of a substring of text.
type Range = std::ops::Range<usize>;
@@ -243,7 +221,7 @@ type Range = std::ops::Range<usize>;
// The characters by which spacing, inline content and pins are replaced in the
// paragraph's full text.
const SPACING_REPLACE: char = ' '; // Space
-const NODE_REPLACE: char = '\u{FFFC}'; // Object Replacement Character
+const OBJ_REPLACE: char = '\u{FFFC}'; // Object Replacement Character
/// A paragraph representation in which children are already layouted and text
/// is already preshaped.
@@ -254,7 +232,7 @@ const NODE_REPLACE: char = '\u{FFFC}'; // Object Replacement Character
struct Preparation<'a> {
/// Bidirectional text embedding levels for the paragraph.
bidi: BidiInfo<'a>,
- /// Text runs, spacing and layouted nodes.
+ /// Text runs, spacing and layouted elements.
items: Vec<Item<'a>>,
/// The span mapper.
spans: SpanMapper,
@@ -325,9 +303,9 @@ enum Segment<'a> {
/// Horizontal spacing between other segments.
Spacing(Spacing),
/// A mathematical equation.
- Equation(&'a EquationNode),
+ Equation(&'a EquationElem),
/// A box with arbitrary content.
- Box(&'a BoxNode, bool),
+ Box(&'a BoxElem, bool),
/// Metadata.
Meta,
}
@@ -339,7 +317,7 @@ impl Segment<'_> {
Self::Text(len) => len,
Self::Spacing(_) => SPACING_REPLACE.len_utf8(),
Self::Box(_, true) => SPACING_REPLACE.len_utf8(),
- Self::Equation(_) | Self::Box(_, _) | Self::Meta => NODE_REPLACE.len_utf8(),
+ Self::Equation(_) | Self::Box(_, _) | Self::Meta => OBJ_REPLACE.len_utf8(),
}
}
}
@@ -352,7 +330,7 @@ enum Item<'a> {
/// Absolute spacing between other items.
Absolute(Abs),
/// Fractional spacing between other items.
- Fractional(Fr, Option<(&'a BoxNode, StyleChain<'a>)>),
+ Fractional(Fr, Option<(&'a BoxElem, StyleChain<'a>)>),
/// Layouted inline-level content.
Frame(Frame),
}
@@ -371,7 +349,7 @@ impl<'a> Item<'a> {
match self {
Self::Text(shaped) => shaped.text.len(),
Self::Absolute(_) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(),
- Self::Frame(_) => NODE_REPLACE.len_utf8(),
+ Self::Frame(_) => OBJ_REPLACE.len_utf8(),
}
}
@@ -520,7 +498,7 @@ fn collect<'a>(
let mut iter = children.iter().peekable();
if consecutive {
- let first_line_indent = ParNode::first_line_indent_in(*styles);
+ let first_line_indent = ParElem::first_line_indent_in(*styles);
if !first_line_indent.is_zero()
&& children
.iter()
@@ -529,7 +507,7 @@ fn collect<'a>(
behaved.behaviour() == Behaviour::Ignorant
}) {
None
- } else if child.is::<TextNode>() || child.is::<SmartQuoteNode>() {
+ } else if child.is::<TextElem>() || child.is::<SmartQuoteElem>() {
Some(true)
} else {
Some(false)
@@ -542,7 +520,7 @@ fn collect<'a>(
}
}
- let hang = ParNode::hanging_indent_in(*styles);
+ let hang = ParElem::hanging_indent_in(*styles);
if !hang.is_zero() {
full.push(SPACING_REPLACE);
segments.push((Segment::Spacing((-hang).into()), *styles));
@@ -551,61 +529,61 @@ fn collect<'a>(
while let Some(mut child) = iter.next() {
let outer = styles;
let mut styles = *styles;
- if let Some(node) = child.to::<StyledNode>() {
- child = Box::leak(Box::new(node.body()));
- styles = outer.chain(Box::leak(Box::new(node.styles())));
+ if let Some((elem, local)) = child.to_styled() {
+ child = elem;
+ styles = outer.chain(local);
}
- let segment = if child.is::<SpaceNode>() {
+ let segment = if child.is::<SpaceElem>() {
full.push(' ');
Segment::Text(1)
- } else if let Some(node) = child.to::<TextNode>() {
+ } else if let Some(elem) = child.to::<TextElem>() {
let prev = full.len();
- if let Some(case) = TextNode::case_in(styles) {
- full.push_str(&case.apply(&node.text()));
+ if let Some(case) = TextElem::case_in(styles) {
+ full.push_str(&case.apply(&elem.text()));
} else {
- full.push_str(&node.text());
+ full.push_str(&elem.text());
}
Segment::Text(full.len() - prev)
- } else if let Some(node) = child.to::<HNode>() {
+ } else if let Some(elem) = child.to::<HElem>() {
full.push(SPACING_REPLACE);
- Segment::Spacing(node.amount())
- } else if let Some(node) = child.to::<LinebreakNode>() {
- let c = if node.justify(styles) { '\u{2028}' } else { '\n' };
+ Segment::Spacing(elem.amount())
+ } else if let Some(elem) = child.to::<LinebreakElem>() {
+ let c = if elem.justify(styles) { '\u{2028}' } else { '\n' };
full.push(c);
Segment::Text(c.len_utf8())
- } else if let Some(node) = child.to::<SmartQuoteNode>() {
+ } else if let Some(elem) = child.to::<SmartQuoteElem>() {
let prev = full.len();
- if SmartQuoteNode::enabled_in(styles) {
- let lang = TextNode::lang_in(styles);
- let region = TextNode::region_in(styles);
+ if SmartQuoteElem::enabled_in(styles) {
+ let lang = TextElem::lang_in(styles);
+ let region = TextElem::region_in(styles);
let quotes = Quotes::from_lang(lang, region);
let peeked = iter.peek().and_then(|child| {
- if let Some(node) = child.to::<TextNode>() {
- node.text().chars().next()
- } else if child.is::<SmartQuoteNode>() {
+ if let Some(elem) = child.to::<TextElem>() {
+ elem.text().chars().next()
+ } else if child.is::<SmartQuoteElem>() {
Some('"')
- } else if child.is::<SpaceNode>() || child.is::<HNode>() {
+ } else if child.is::<SpaceElem>() || child.is::<HElem>() {
Some(SPACING_REPLACE)
} else {
- Some(NODE_REPLACE)
+ Some(OBJ_REPLACE)
}
});
- full.push_str(quoter.quote(&quotes, node.double(styles), peeked));
+ full.push_str(quoter.quote(&quotes, elem.double(styles), peeked));
} else {
- full.push(if node.double(styles) { '"' } else { '\'' });
+ full.push(if elem.double(styles) { '"' } else { '\'' });
}
Segment::Text(full.len() - prev)
- } else if let Some(node) = child.to::<EquationNode>() {
- full.push(NODE_REPLACE);
- Segment::Equation(node)
- } else if let Some(node) = child.to::<BoxNode>() {
- let frac = node.width(styles).is_fractional();
- full.push(if frac { SPACING_REPLACE } else { NODE_REPLACE });
- Segment::Box(node, frac)
- } else if child.is::<MetaNode>() {
- full.push(NODE_REPLACE);
+ } else if let Some(elem) = child.to::<EquationElem>() {
+ full.push(OBJ_REPLACE);
+ Segment::Equation(elem)
+ } else if let Some(elem) = child.to::<BoxElem>() {
+ let frac = elem.width(styles).is_fractional();
+ full.push(if frac { SPACING_REPLACE } else { OBJ_REPLACE });
+ Segment::Box(elem, frac)
+ } else if child.is::<MetaElem>() {
+ full.push(OBJ_REPLACE);
Segment::Meta
} else {
bail!(child.span(), "unexpected paragraph child");
@@ -645,7 +623,7 @@ fn prepare<'a>(
) -> SourceResult<Preparation<'a>> {
let bidi = BidiInfo::new(
text,
- match TextNode::dir_in(styles) {
+ match TextElem::dir_in(styles) {
Dir::LTR => Some(BidiLevel::ltr()),
Dir::RTL => Some(BidiLevel::rtl()),
_ => None,
@@ -674,16 +652,16 @@ fn prepare<'a>(
Segment::Equation(equation) => {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = equation.layout(vt, styles, pod)?.into_frame();
- frame.translate(Point::with_y(TextNode::baseline_in(styles)));
+ frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
}
- Segment::Box(node, _) => {
- if let Sizing::Fr(v) = node.width(styles) {
- items.push(Item::Fractional(v, Some((node, styles))));
+ Segment::Box(elem, _) => {
+ if let Sizing::Fr(v) = elem.width(styles) {
+ items.push(Item::Fractional(v, Some((elem, styles))));
} else {
let pod = Regions::one(region, Axes::splat(false));
- let mut frame = node.layout(vt, styles, pod)?.into_frame();
- frame.translate(Point::with_y(TextNode::baseline_in(styles)));
+ let mut frame = elem.layout(vt, styles, pod)?.into_frame();
+ frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
}
}
@@ -702,11 +680,11 @@ fn prepare<'a>(
items,
spans,
styles,
- hyphenate: shared_get(styles, children, TextNode::hyphenate_in),
- lang: shared_get(styles, children, TextNode::lang_in),
- align: AlignNode::alignment_in(styles).x.resolve(styles),
- justify: ParNode::justify_in(styles),
- hang: ParNode::hanging_indent_in(styles),
+ hyphenate: shared_get(styles, children, TextElem::hyphenate_in),
+ lang: shared_get(styles, children, TextElem::lang_in),
+ align: AlignElem::alignment_in(styles).x.resolve(styles),
+ justify: ParElem::justify_in(styles),
+ hang: ParElem::hanging_indent_in(styles),
})
}
@@ -775,15 +753,15 @@ fn shared_get<'a, T: PartialEq>(
let value = getter(styles);
children
.iter()
- .filter_map(|child| child.to::<StyledNode>())
- .all(|node| getter(styles.chain(&node.styles())) == value)
+ .filter_map(|child| child.to_styled())
+ .all(|(_, local)| getter(styles.chain(&local)) == value)
.then(|| value)
}
/// Find suitable linebreaks.
fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
- let linebreaks = ParNode::linebreaks_in(p.styles).unwrap_or_else(|| {
- if ParNode::justify_in(p.styles) {
+ let linebreaks = ParElem::linebreaks_in(p.styles).unwrap_or_else(|| {
+ if ParElem::justify_in(p.styles) {
Linebreaks::Optimized
} else {
Linebreaks::Simple
@@ -881,7 +859,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
line: line(vt, p, 0..0, false, false),
}];
- let em = TextNode::size_in(p.styles);
+ let em = TextElem::size_in(p.styles);
for (end, mandatory, hyphen) in breakpoints(p) {
let k = table.len();
@@ -1046,7 +1024,7 @@ impl Breakpoints<'_> {
.hyphenate
.or_else(|| {
let shaped = self.p.find(offset)?.text()?;
- Some(TextNode::hyphenate_in(shaped.styles))
+ Some(TextElem::hyphenate_in(shaped.styles))
})
.unwrap_or(false)
}
@@ -1055,7 +1033,7 @@ impl Breakpoints<'_> {
fn lang(&self, offset: usize) -> Option<hypher::Lang> {
let lang = self.p.lang.or_else(|| {
let shaped = self.p.find(offset)?.text()?;
- Some(TextNode::lang_in(shaped.styles))
+ Some(TextElem::lang_in(shaped.styles))
})?;
let bytes = lang.as_str().as_bytes().try_into().ok()?;
@@ -1196,7 +1174,7 @@ fn finalize(
.collect::<SourceResult<_>>()?;
// Prevent orphans.
- let leading = ParNode::leading_in(p.styles);
+ let leading = ParElem::leading_in(p.styles);
if frames.len() >= 2 && !frames[1].is_empty() {
let second = frames.remove(1);
let first = &mut frames[0];
@@ -1243,7 +1221,7 @@ fn commit(
if let Some(Item::Text(text)) = reordered.first() {
if let Some(glyph) = text.glyphs.first() {
if !text.dir.is_positive()
- && TextNode::overhang_in(text.styles)
+ && TextElem::overhang_in(text.styles)
&& (reordered.len() > 1 || text.glyphs.len() > 1)
{
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
@@ -1257,7 +1235,7 @@ fn commit(
if let Some(Item::Text(text)) = reordered.last() {
if let Some(glyph) = text.glyphs.last() {
if text.dir.is_positive()
- && TextNode::overhang_in(text.styles)
+ && TextElem::overhang_in(text.styles)
&& (reordered.len() > 1 || text.glyphs.len() > 1)
{
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
@@ -1295,13 +1273,13 @@ fn commit(
Item::Absolute(v) => {
offset += *v;
}
- Item::Fractional(v, node) => {
+ Item::Fractional(v, elem) => {
let amount = v.share(fr, remaining);
- if let Some((node, styles)) = node {
+ if let Some((elem, styles)) = elem {
let region = Size::new(amount, full);
let pod = Regions::one(region, Axes::new(true, false));
- let mut frame = node.layout(vt, *styles, pod)?.into_frame();
- frame.translate(Point::with_y(TextNode::baseline_in(*styles)));
+ let mut frame = elem.layout(vt, *styles, pod)?.into_frame();
+ frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
push(&mut offset, frame);
} else {
offset += amount;
diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs
index bfabd0f3..057278df 100644
--- a/library/src/layout/place.rs
+++ b/library/src/layout/place.rs
@@ -23,8 +23,8 @@ use crate::prelude::*;
///
/// Display: Place
/// Category: layout
-#[node(Layout, Behave)]
-pub struct PlaceNode {
+#[element(Layout, Behave)]
+pub struct PlaceElem {
/// Relative to which position in the parent container to place the content.
///
/// When an axis of the page is `{auto}` sized, all alignments relative to that
@@ -53,7 +53,7 @@ pub struct PlaceNode {
pub body: Content,
}
-impl Layout for PlaceNode {
+impl Layout for PlaceElem {
fn layout(
&self,
vt: &mut Vt,
@@ -86,16 +86,16 @@ impl Layout for PlaceNode {
}
}
-impl PlaceNode {
- /// Whether this node wants to be placed relative to its its parent's base
- /// origin. Instead of relative to the parent's current flow/cursor
+impl PlaceElem {
+ /// Whether this element wants to be placed relative to its its parent's
+ /// base origin. Instead of relative to the parent's current flow/cursor
/// position.
pub fn out_of_flow(&self, styles: StyleChain) -> bool {
self.alignment(styles).y.is_some()
}
}
-impl Behave for PlaceNode {
+impl Behave for PlaceElem {
fn behaviour(&self) -> Behaviour {
Behaviour::Ignorant
}
diff --git a/library/src/layout/regions.rs b/library/src/layout/regions.rs
index 94c81704..5a4db178 100644
--- a/library/src/layout/regions.rs
+++ b/library/src/layout/regions.rs
@@ -14,8 +14,8 @@ pub struct Regions<'a> {
/// The height of the final region that is repeated once the backlog is
/// drained. The width is the same for all regions.
pub last: Option<Abs>,
- /// Whether nodes should expand to fill the regions instead of shrinking to
- /// fit the content.
+ /// Whether elements should expand to fill the regions instead of shrinking
+ /// to fit the content.
pub expand: Axes<bool>,
}
diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs
index c8f63ac3..a44bd075 100644
--- a/library/src/layout/repeat.rs
+++ b/library/src/layout/repeat.rs
@@ -1,6 +1,6 @@
use crate::prelude::*;
-use super::AlignNode;
+use super::AlignElem;
/// Repeats content to the available space.
///
@@ -23,14 +23,14 @@ use super::AlignNode;
///
/// Display: Repeat
/// Category: layout
-#[node(Layout)]
-pub struct RepeatNode {
+#[element(Layout)]
+pub struct RepeatElem {
/// The content to repeat.
#[required]
pub body: Content,
}
-impl Layout for RepeatNode {
+impl Layout for RepeatElem {
fn layout(
&self,
vt: &mut Vt,
@@ -39,7 +39,7 @@ impl Layout for RepeatNode {
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.size, Axes::new(false, false));
let piece = self.body().layout(vt, styles, pod)?.into_frame();
- let align = AlignNode::alignment_in(styles).x.resolve(styles);
+ let align = AlignElem::alignment_in(styles).x.resolve(styles);
let fill = regions.size.x;
let width = piece.width();
diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs
index e67fec03..9253c497 100644
--- a/library/src/layout/spacing.rs
+++ b/library/src/layout/spacing.rs
@@ -21,8 +21,8 @@ use crate::prelude::*;
///
/// Display: Spacing (H)
/// Category: layout
-#[node(Behave)]
-pub struct HNode {
+#[element(Behave)]
+pub struct HElem {
/// How much spacing to insert.
#[required]
pub amount: Spacing,
@@ -45,7 +45,7 @@ pub struct HNode {
pub weak: bool,
}
-impl Behave for HNode {
+impl Behave for HElem {
fn behaviour(&self) -> Behaviour {
if self.amount().is_fractional() {
Behaviour::Destructive
@@ -85,8 +85,8 @@ impl Behave for HNode {
///
/// Display: Spacing (V)
/// Category: layout
-#[node(Behave)]
-pub struct VNode {
+#[element(Behave)]
+pub struct VElem {
/// How much spacing to insert.
#[required]
pub amount: Spacing,
@@ -107,13 +107,13 @@ pub struct VNode {
#[external]
pub weak: bool,
- /// The node's weakness level, see also [`Behaviour`].
+ /// The elements's weakness level, see also [`Behaviour`].
#[internal]
#[parse(args.named("weak")?.map(|v: bool| v as usize))]
pub weakness: usize,
}
-impl VNode {
+impl VElem {
/// Normal strong spacing.
pub fn strong(amount: Spacing) -> Self {
Self::new(amount).with_weakness(0)
@@ -129,18 +129,18 @@ impl VNode {
Self::new(amount).with_weakness(2)
}
- /// Weak spacing with BlockNode::ABOVE/BELOW weakness.
+ /// Weak spacing with BlockElem::ABOVE/BELOW weakness.
pub fn block_around(amount: Spacing) -> Self {
Self::new(amount).with_weakness(3)
}
- /// Weak spacing with BlockNode::SPACING weakness.
+ /// Weak spacing with BlockElem::SPACING weakness.
pub fn block_spacing(amount: Spacing) -> Self {
Self::new(amount).with_weakness(4)
}
}
-impl Behave for VNode {
+impl Behave for VElem {
fn behaviour(&self) -> Behaviour {
if self.amount().is_fractional() {
Behaviour::Destructive
@@ -158,8 +158,8 @@ impl Behave for VNode {
}
cast_from_value! {
- VNode,
- v: Content => v.to::<Self>().cloned().ok_or("expected vnode")?,
+ VElem,
+ v: Content => v.to::<Self>().cloned().ok_or("expected `v` element")?,
}
/// Kinds of spacing.
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index 1dd81a60..77cd3f8f 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -1,6 +1,4 @@
-use typst::model::StyledNode;
-
-use super::{AlignNode, Spacing};
+use super::{AlignElem, Spacing};
use crate::prelude::*;
/// Arrange content and spacing horizontally or vertically.
@@ -20,8 +18,8 @@ use crate::prelude::*;
///
/// Display: Stack
/// Category: layout
-#[node(Layout)]
-pub struct StackNode {
+#[element(Layout)]
+pub struct StackElem {
/// The direction along which the items are stacked. Possible values are:
///
/// - `{ltr}`: Left to right.
@@ -39,7 +37,7 @@ pub struct StackNode {
pub children: Vec<StackChild>,
}
-impl Layout for StackNode {
+impl Layout for StackElem {
fn layout(
&self,
vt: &mut Vt,
@@ -73,7 +71,7 @@ impl Layout for StackNode {
}
}
-/// A child of a stack node.
+/// A child of a stack element.
#[derive(Hash)]
pub enum StackChild {
/// Spacing between other children.
@@ -196,14 +194,13 @@ impl<'a> StackLayouter<'a> {
self.finish_region();
}
- // Block-axis alignment of the `AlignNode` is respected
- // by the stack node.
- let aligns = if let Some(align) = block.to::<AlignNode>() {
+ // Block-axis alignment of the `AlignElement` is respected by stacks.
+ let aligns = if let Some(align) = block.to::<AlignElem>() {
align.alignment(styles)
- } else if let Some(styled) = block.to::<StyledNode>() {
- AlignNode::alignment_in(styles.chain(&styled.styles()))
+ } else if let Some((_, local)) = block.to_styled() {
+ AlignElem::alignment_in(styles.chain(&local))
} else {
- AlignNode::alignment_in(styles)
+ AlignElem::alignment_in(styles)
}
.resolve(styles);
diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs
index d4b6e7d7..809c7ea7 100644
--- a/library/src/layout/table.rs
+++ b/library/src/layout/table.rs
@@ -1,4 +1,4 @@
-use crate::layout::{AlignNode, GridLayouter, TrackSizings};
+use crate::layout::{AlignElem, GridLayouter, TrackSizings};
use crate::meta::LocalName;
use crate::prelude::*;
@@ -32,8 +32,8 @@ use crate::prelude::*;
///
/// Display: Table
/// Category: layout
-#[node(Layout, LocalName)]
-pub struct TableNode {
+#[element(Layout, LocalName)]
+pub struct TableElem {
/// Defines the column sizes. See the [grid documentation]($func/grid) for
/// more information on track sizing.
pub columns: TrackSizings,
@@ -109,7 +109,7 @@ pub struct TableNode {
pub children: Vec<Content>,
}
-impl Layout for TableNode {
+impl Layout for TableElem {
fn layout(
&self,
vt: &mut Vt,
@@ -132,7 +132,7 @@ impl Layout for TableNode {
let x = i % cols;
let y = i / cols;
if let Smart::Custom(alignment) = align.resolve(vt, x, y)? {
- child = child.styled(AlignNode::set_alignment(alignment));
+ child = child.styled(AlignElem::set_alignment(alignment));
}
Ok(child)
@@ -168,7 +168,7 @@ impl Layout for TableNode {
let hline = Geometry::Line(target).stroked(stroke);
frame.prepend(
Point::new(-half, offset),
- Element::Shape(hline, self.span()),
+ FrameItem::Shape(hline, self.span()),
);
}
@@ -178,7 +178,7 @@ impl Layout for TableNode {
let vline = Geometry::Line(target).stroked(stroke);
frame.prepend(
Point::new(offset, -half),
- Element::Shape(vline, self.span()),
+ FrameItem::Shape(vline, self.span()),
);
}
}
@@ -192,7 +192,7 @@ impl Layout for TableNode {
let pos = Point::new(dx, dy);
let size = Size::new(col, row.height);
let rect = Geometry::Rect(size).filled(fill);
- frame.prepend(pos, Element::Shape(rect, self.span()));
+ frame.prepend(pos, FrameItem::Shape(rect, self.span()));
}
dy += row.height;
}
@@ -271,7 +271,7 @@ impl<T: Into<Value>> From<Celled<T>> for Value {
}
}
-impl LocalName for TableNode {
+impl LocalName for TableElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Tabelle",
diff --git a/library/src/layout/terms.rs b/library/src/layout/terms.rs
index 853dd32d..1200076f 100644
--- a/library/src/layout/terms.rs
+++ b/library/src/layout/terms.rs
@@ -1,7 +1,7 @@
-use super::{HNode, VNode};
-use crate::layout::{BlockNode, ParNode, Spacing};
+use super::{HElem, VElem};
+use crate::layout::{BlockElem, ParElem, Spacing};
use crate::prelude::*;
-use crate::text::{SpaceNode, TextNode};
+use crate::text::{SpaceElem, TextElem};
/// A list of terms and their descriptions.
///
@@ -22,8 +22,8 @@ use crate::text::{SpaceNode, TextNode};
///
/// Display: Term List
/// Category: layout
-#[node(Layout)]
-pub struct TermsNode {
+#[element(Layout)]
+pub struct TermsElem {
/// If this is `{false}`, the items are spaced apart with [term list
/// spacing]($func/terms.spacing). If it is `{true}`, they use normal
/// [leading]($func/par.leading) instead. This makes the term list more
@@ -76,7 +76,7 @@ pub struct TermsNode {
pub children: Vec<TermItem>,
}
-impl Layout for TermsNode {
+impl Layout for TermsElem {
fn layout(
&self,
vt: &mut Vt,
@@ -86,27 +86,27 @@ impl Layout for TermsNode {
let indent = self.indent(styles);
let hanging_indent = self.hanging_indent(styles);
let gutter = if self.tight(styles) {
- ParNode::leading_in(styles).into()
+ ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockNode::below_in(styles).amount())
+ .unwrap_or_else(|| BlockElem::below_in(styles).amount())
};
let mut seq = vec![];
for (i, child) in self.children().into_iter().enumerate() {
if i > 0 {
- seq.push(VNode::new(gutter).with_weakness(1).pack());
+ seq.push(VElem::new(gutter).with_weakness(1).pack());
}
if indent.is_zero() {
- seq.push(HNode::new(indent.into()).pack());
+ seq.push(HElem::new(indent.into()).pack());
}
- seq.push((child.term() + TextNode::packed(':')).strong());
- seq.push(SpaceNode::new().pack());
+ seq.push((child.term() + TextElem::packed(':')).strong());
+ seq.push(SpaceElem::new().pack());
seq.push(child.description());
}
Content::sequence(seq)
- .styled(ParNode::set_hanging_indent(hanging_indent + indent))
+ .styled(ParElem::set_hanging_indent(hanging_indent + indent))
.layout(vt, styles, regions)
}
}
@@ -115,7 +115,7 @@ impl Layout for TermsNode {
///
/// Display: Term List Item
/// Category: layout
-#[node]
+#[element]
pub struct TermItem {
/// The term described by the list item.
#[required]
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs
index 2afe8201..2045e9ed 100644
--- a/library/src/layout/transform.rs
+++ b/library/src/layout/transform.rs
@@ -23,8 +23,8 @@ use crate::prelude::*;
///
/// Display: Move
/// Category: layout
-#[node(Layout)]
-pub struct MoveNode {
+#[element(Layout)]
+pub struct MoveElem {
/// The horizontal displacement of the content.
pub dx: Rel<Length>,
@@ -36,7 +36,7 @@ pub struct MoveNode {
pub body: Content,
}
-impl Layout for MoveNode {
+impl Layout for MoveElem {
fn layout(
&self,
vt: &mut Vt,
@@ -69,8 +69,8 @@ impl Layout for MoveNode {
///
/// Display: Rotate
/// Category: layout
-#[node(Layout)]
-pub struct RotateNode {
+#[element(Layout)]
+pub struct RotateElem {
/// The amount of rotation.
///
/// ```example
@@ -104,7 +104,7 @@ pub struct RotateNode {
pub body: Content,
}
-impl Layout for RotateNode {
+impl Layout for RotateElem {
fn layout(
&self,
vt: &mut Vt,
@@ -137,8 +137,8 @@ impl Layout for RotateNode {
///
/// Display: Scale
/// Category: layout
-#[node(Layout)]
-pub struct ScaleNode {
+#[element(Layout)]
+pub struct ScaleElem {
/// The horizontal scaling factor.
///
/// The body will be mirrored horizontally if the parameter is negative.
@@ -172,7 +172,7 @@ pub struct ScaleNode {
pub body: Content,
}
-impl Layout for ScaleNode {
+impl Layout for ScaleElem {
fn layout(
&self,
vt: &mut Vt,
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 2f951b92..1a998700 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -13,7 +13,7 @@ pub mod visualize;
use typst::diag::At;
use typst::eval::{LangItems, Library, Module, Scope};
use typst::geom::{Align, Color, Dir, GenAlign, Smart};
-use typst::model::{Node, NodeId, StyleMap};
+use typst::model::{Element, Styles};
use self::layout::LayoutRoot;
@@ -30,69 +30,69 @@ fn global(math: Module, calc: Module) -> Module {
let mut global = Scope::deduplicating();
// Text.
- global.define("text", text::TextNode::id());
- global.define("linebreak", text::LinebreakNode::id());
- global.define("smartquote", text::SmartQuoteNode::id());
- global.define("strong", text::StrongNode::id());
- global.define("emph", text::EmphNode::id());
+ global.define("text", text::TextElem::func());
+ global.define("linebreak", text::LinebreakElem::func());
+ global.define("smartquote", text::SmartQuoteElem::func());
+ global.define("strong", text::StrongElem::func());
+ global.define("emph", text::EmphElem::func());
global.define("lower", text::lower);
global.define("upper", text::upper);
global.define("smallcaps", text::smallcaps);
- global.define("sub", text::SubNode::id());
- global.define("super", text::SuperNode::id());
- global.define("underline", text::UnderlineNode::id());
- global.define("strike", text::StrikeNode::id());
- global.define("overline", text::OverlineNode::id());
- global.define("raw", text::RawNode::id());
+ global.define("sub", text::SubElem::func());
+ global.define("super", text::SuperElem::func());
+ global.define("underline", text::UnderlineElem::func());
+ global.define("strike", text::StrikeElem::func());
+ global.define("overline", text::OverlineElem::func());
+ global.define("raw", text::RawElem::func());
global.define("lorem", text::lorem);
// Math.
global.define("math", math);
// Layout.
- global.define("page", layout::PageNode::id());
- global.define("pagebreak", layout::PagebreakNode::id());
- global.define("v", layout::VNode::id());
- global.define("par", layout::ParNode::id());
- global.define("parbreak", layout::ParbreakNode::id());
- global.define("h", layout::HNode::id());
- global.define("box", layout::BoxNode::id());
- global.define("block", layout::BlockNode::id());
- global.define("list", layout::ListNode::id());
- global.define("enum", layout::EnumNode::id());
- global.define("terms", layout::TermsNode::id());
- global.define("table", layout::TableNode::id());
- global.define("stack", layout::StackNode::id());
- global.define("grid", layout::GridNode::id());
- global.define("columns", layout::ColumnsNode::id());
- global.define("colbreak", layout::ColbreakNode::id());
- global.define("place", layout::PlaceNode::id());
- global.define("align", layout::AlignNode::id());
- global.define("pad", layout::PadNode::id());
- global.define("repeat", layout::RepeatNode::id());
- global.define("move", layout::MoveNode::id());
- global.define("scale", layout::ScaleNode::id());
- global.define("rotate", layout::RotateNode::id());
- global.define("hide", layout::HideNode::id());
+ global.define("page", layout::PageElem::func());
+ global.define("pagebreak", layout::PagebreakElem::func());
+ global.define("v", layout::VElem::func());
+ global.define("par", layout::ParElem::func());
+ global.define("parbreak", layout::ParbreakElem::func());
+ global.define("h", layout::HElem::func());
+ global.define("box", layout::BoxElem::func());
+ global.define("block", layout::BlockElem::func());
+ global.define("list", layout::ListElem::func());
+ global.define("enum", layout::EnumElem::func());
+ global.define("terms", layout::TermsElem::func());
+ global.define("table", layout::TableElem::func());
+ global.define("stack", layout::StackElem::func());
+ global.define("grid", layout::GridElem::func());
+ global.define("columns", layout::ColumnsElem::func());
+ global.define("colbreak", layout::ColbreakElem::func());
+ global.define("place", layout::PlaceElem::func());
+ global.define("align", layout::AlignElem::func());
+ global.define("pad", layout::PadElem::func());
+ global.define("repeat", layout::RepeatElem::func());
+ global.define("move", layout::MoveElem::func());
+ global.define("scale", layout::ScaleElem::func());
+ global.define("rotate", layout::RotateElem::func());
+ global.define("hide", layout::HideElem::func());
global.define("measure", layout::measure);
// Visualize.
- global.define("image", visualize::ImageNode::id());
- global.define("line", visualize::LineNode::id());
- global.define("rect", visualize::RectNode::id());
- global.define("square", visualize::SquareNode::id());
- global.define("ellipse", visualize::EllipseNode::id());
- global.define("circle", visualize::CircleNode::id());
+ global.define("image", visualize::ImageElem::func());
+ global.define("line", visualize::LineElem::func());
+ global.define("rect", visualize::RectElem::func());
+ global.define("square", visualize::SquareElem::func());
+ global.define("ellipse", visualize::EllipseElem::func());
+ global.define("circle", visualize::CircleElem::func());
// Meta.
- global.define("document", meta::DocumentNode::id());
- global.define("ref", meta::RefNode::id());
- global.define("link", meta::LinkNode::id());
- global.define("outline", meta::OutlineNode::id());
- global.define("heading", meta::HeadingNode::id());
- global.define("figure", meta::FigureNode::id());
- global.define("cite", meta::CiteNode::id());
- global.define("bibliography", meta::BibliographyNode::id());
+ global.define("document", meta::DocumentElem::func());
+ global.define("ref", meta::RefElem::func());
+ global.define("link", meta::LinkElem::func());
+ global.define("outline", meta::OutlineElem::func());
+ global.define("heading", meta::HeadingElem::func());
+ global.define("figure", meta::FigureElem::func());
+ global.define("cite", meta::CiteElem::func());
+ global.define("bibliography", meta::BibliographyElem::func());
global.define("locate", meta::locate);
global.define("style", meta::style);
global.define("counter", meta::counter);
@@ -166,71 +166,71 @@ fn global(math: Module, calc: Module) -> Module {
}
/// Construct the standard style map.
-fn styles() -> StyleMap {
- StyleMap::new()
+fn styles() -> Styles {
+ Styles::new()
}
/// Construct the standard lang item mapping.
fn items() -> LangItems {
LangItems {
layout: |world, content, styles| content.layout_root(world, styles),
- em: text::TextNode::size_in,
- dir: text::TextNode::dir_in,
- space: || text::SpaceNode::new().pack(),
- linebreak: || text::LinebreakNode::new().pack(),
- text: |text| text::TextNode::new(text).pack(),
- text_id: NodeId::of::<text::TextNode>(),
- text_str: |content| Some(content.to::<text::TextNode>()?.text()),
- smart_quote: |double| text::SmartQuoteNode::new().with_double(double).pack(),
- parbreak: || layout::ParbreakNode::new().pack(),
- strong: |body| text::StrongNode::new(body).pack(),
- emph: |body| text::EmphNode::new(body).pack(),
+ em: text::TextElem::size_in,
+ dir: text::TextElem::dir_in,
+ space: || text::SpaceElem::new().pack(),
+ linebreak: || text::LinebreakElem::new().pack(),
+ text: |text| text::TextElem::new(text).pack(),
+ text_func: text::TextElem::func(),
+ text_str: |content| Some(content.to::<text::TextElem>()?.text()),
+ smart_quote: |double| text::SmartQuoteElem::new().with_double(double).pack(),
+ parbreak: || layout::ParbreakElem::new().pack(),
+ strong: |body| text::StrongElem::new(body).pack(),
+ emph: |body| text::EmphElem::new(body).pack(),
raw: |text, lang, block| {
- let mut node = text::RawNode::new(text).with_block(block);
+ let mut elem = text::RawElem::new(text).with_block(block);
if let Some(lang) = lang {
- node.push_lang(Some(lang));
+ elem.push_lang(Some(lang));
}
- node.pack()
+ elem.pack()
},
- raw_languages: text::RawNode::languages,
- link: |url| meta::LinkNode::from_url(url).pack(),
+ raw_languages: text::RawElem::languages,
+ link: |url| meta::LinkElem::from_url(url).pack(),
reference: |target, supplement| {
- let mut node = meta::RefNode::new(target);
+ let mut elem = meta::RefElem::new(target);
if let Some(supplement) = supplement {
- node.push_supplement(Smart::Custom(Some(meta::Supplement::Content(
+ elem.push_supplement(Smart::Custom(Some(meta::Supplement::Content(
supplement,
))));
}
- node.pack()
+ elem.pack()
},
- bibliography_keys: meta::BibliographyNode::keys,
- heading: |level, title| meta::HeadingNode::new(title).with_level(level).pack(),
+ bibliography_keys: meta::BibliographyElem::keys,
+ heading: |level, title| meta::HeadingElem::new(title).with_level(level).pack(),
list_item: |body| layout::ListItem::new(body).pack(),
enum_item: |number, body| {
- let mut node = layout::EnumItem::new(body);
+ let mut elem = layout::EnumItem::new(body);
if let Some(number) = number {
- node.push_number(Some(number));
+ elem.push_number(Some(number));
}
- node.pack()
+ elem.pack()
},
term_item: |term, description| layout::TermItem::new(term, description).pack(),
- equation: |body, block| math::EquationNode::new(body).with_block(block).pack(),
- math_align_point: || math::AlignPointNode::new().pack(),
- math_delimited: |open, body, close| math::LrNode::new(open + body + close).pack(),
+ equation: |body, block| math::EquationElem::new(body).with_block(block).pack(),
+ math_align_point: || math::AlignPointElem::new().pack(),
+ math_delimited: |open, body, close| math::LrElem::new(open + body + close).pack(),
math_attach: |base, bottom, top| {
- let mut node = math::AttachNode::new(base);
+ let mut elem = math::AttachElem::new(base);
if let Some(bottom) = bottom {
- node.push_bottom(Some(bottom));
+ elem.push_bottom(Some(bottom));
}
if let Some(top) = top {
- node.push_top(Some(top));
+ elem.push_top(Some(top));
}
- node.pack()
+ elem.pack()
},
math_accent: |base, accent| {
- math::AccentNode::new(base, math::Accent::new(accent)).pack()
+ math::AccentElem::new(base, math::Accent::new(accent)).pack()
},
- math_frac: |num, denom| math::FracNode::new(num, denom).pack(),
+ math_frac: |num, denom| math::FracElem::new(num, denom).pack(),
library_method: |vm, dynamic, method, args, span| {
if let Some(counter) = dynamic.downcast::<meta::Counter>().cloned() {
counter.call_method(vm, method, args, span)
diff --git a/library/src/math/accent.rs b/library/src/math/accent.rs
index 9ef76279..471507c5 100644
--- a/library/src/math/accent.rs
+++ b/library/src/math/accent.rs
@@ -1,5 +1,3 @@
-use typst::eval::combining_accent;
-
use super::*;
/// How much the accent can be shorter than the base.
@@ -16,8 +14,8 @@ const ACCENT_SHORT_FALL: Em = Em::new(0.5);
///
/// Display: Accent
/// Category: math
-#[node(LayoutMath)]
-pub struct AccentNode {
+#[element(LayoutMath)]
+pub struct AccentElem {
/// The base to which the accent is applied.
/// May consist of multiple letters.
///
@@ -50,7 +48,7 @@ pub struct AccentNode {
pub accent: Accent,
}
-impl LayoutMath for AccentNode {
+impl LayoutMath for AccentElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_cramped(true));
let base = ctx.layout_fragment(&self.base())?;
@@ -116,15 +114,15 @@ pub struct Accent(char);
impl Accent {
/// Normalize a character into an accent.
pub fn new(c: char) -> Self {
- Self(combining_accent(c).unwrap_or(c))
+ Self(Symbol::combining_accent(c).unwrap_or(c))
}
}
cast_from_value! {
Accent,
v: char => Self::new(v),
- v: Content => match v.to::<TextNode>() {
- Some(node) => Value::Str(node.text().into()).cast()?,
+ v: Content => match v.to::<TextElem>() {
+ Some(elem) => Value::Str(elem.text().into()).cast()?,
None => Err("expected text")?,
},
}
diff --git a/library/src/math/align.rs b/library/src/math/align.rs
index 6cf13a0f..d34379e2 100644
--- a/library/src/math/align.rs
+++ b/library/src/math/align.rs
@@ -4,10 +4,10 @@ use super::*;
///
/// Display: Alignment Point
/// Category: math
-#[node(LayoutMath)]
-pub struct AlignPointNode {}
+#[element(LayoutMath)]
+pub struct AlignPointElem {}
-impl LayoutMath for AlignPointNode {
+impl LayoutMath for AlignPointElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.push(MathFragment::Align);
Ok(())
diff --git a/library/src/math/attach.rs b/library/src/math/attach.rs
index 7d8749f2..1b315f77 100644
--- a/library/src/math/attach.rs
+++ b/library/src/math/attach.rs
@@ -13,8 +13,8 @@ use super::*;
///
/// Display: Attachment
/// Category: math
-#[node(LayoutMath)]
-pub struct AttachNode {
+#[element(LayoutMath)]
+pub struct AttachElem {
/// The base to which things are attached.
#[required]
pub base: Content,
@@ -26,25 +26,25 @@ pub struct AttachNode {
pub bottom: Option<Content>,
}
-impl LayoutMath for AttachNode {
+impl LayoutMath for AttachElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let base = self.base();
- let display_limits = base.is::<LimitsNode>();
- let display_scripts = base.is::<ScriptsNode>();
+ let display_limits = base.is::<LimitsElem>();
+ let display_scripts = base.is::<ScriptsElem>();
let base = ctx.layout_fragment(&base)?;
ctx.style(ctx.style.for_subscript());
let top = self
.top(ctx.styles())
- .map(|node| ctx.layout_fragment(&node))
+ .map(|elem| ctx.layout_fragment(&elem))
.transpose()?;
ctx.unstyle();
ctx.style(ctx.style.for_superscript());
let bottom = self
.bottom(ctx.styles())
- .map(|node| ctx.layout_fragment(&node))
+ .map(|elem| ctx.layout_fragment(&elem))
.transpose()?;
ctx.unstyle();
@@ -75,14 +75,14 @@ impl LayoutMath for AttachNode {
///
/// Display: Scripts
/// Category: math
-#[node(LayoutMath)]
-pub struct ScriptsNode {
+#[element(LayoutMath)]
+pub struct ScriptsElem {
/// The base to attach the scripts to.
#[required]
pub body: Content,
}
-impl LayoutMath for ScriptsNode {
+impl LayoutMath for ScriptsElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
self.body().layout_math(ctx)
}
@@ -97,14 +97,14 @@ impl LayoutMath for ScriptsNode {
///
/// Display: Limits
/// Category: math
-#[node(LayoutMath)]
-pub struct LimitsNode {
+#[element(LayoutMath)]
+pub struct LimitsElem {
/// The base to attach the limits to.
#[required]
pub body: Content,
}
-impl LayoutMath for LimitsNode {
+impl LayoutMath for LimitsElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
self.body().layout_math(ctx)
}
diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs
index bd44546d..aed826b5 100644
--- a/library/src/math/ctx.rs
+++ b/library/src/math/ctx.rs
@@ -32,7 +32,7 @@ pub struct MathContext<'a, 'b, 'v> {
pub constants: ttf_parser::math::Constants<'a>,
pub space_width: Em,
pub fragments: Vec<MathFragment>,
- pub map: StyleMap,
+ pub local: Styles,
pub style: MathStyle,
pub size: Abs,
outer: StyleChain<'a>,
@@ -49,7 +49,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
) -> Self {
let table = font.ttf().tables().math.unwrap();
let constants = table.constants.unwrap();
- let size = TextNode::size_in(styles);
+ let size = TextElem::size_in(styles);
let ttf = font.ttf();
let space_width = ttf
.glyph_index(' ')
@@ -67,7 +67,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
constants,
space_width,
fragments: vec![],
- map: StyleMap::new(),
+ local: Styles::new(),
style: MathStyle {
variant: MathVariant::Serif,
size: if block { MathSize::Display } else { MathSize::Text },
@@ -94,39 +94,39 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn layout_fragment(
&mut self,
- node: &dyn LayoutMath,
+ elem: &dyn LayoutMath,
) -> SourceResult<MathFragment> {
- let row = self.layout_fragments(node)?;
+ let row = self.layout_fragments(elem)?;
Ok(MathRow::new(row).to_fragment(self))
}
pub fn layout_fragments(
&mut self,
- node: &dyn LayoutMath,
+ elem: &dyn LayoutMath,
) -> SourceResult<Vec<MathFragment>> {
let prev = std::mem::take(&mut self.fragments);
- node.layout_math(self)?;
+ elem.layout_math(self)?;
Ok(std::mem::replace(&mut self.fragments, prev))
}
- pub fn layout_row(&mut self, node: &dyn LayoutMath) -> SourceResult<MathRow> {
- let fragments = self.layout_fragments(node)?;
+ pub fn layout_row(&mut self, elem: &dyn LayoutMath) -> SourceResult<MathRow> {
+ let fragments = self.layout_fragments(elem)?;
Ok(MathRow::new(fragments))
}
- pub fn layout_frame(&mut self, node: &dyn LayoutMath) -> SourceResult<Frame> {
- Ok(self.layout_fragment(node)?.to_frame())
+ pub fn layout_frame(&mut self, elem: &dyn LayoutMath) -> SourceResult<Frame> {
+ Ok(self.layout_fragment(elem)?.to_frame())
}
pub fn layout_content(&mut self, content: &Content) -> SourceResult<Frame> {
Ok(content
- .layout(&mut self.vt, self.outer.chain(&self.map), self.regions)?
+ .layout(&mut self.vt, self.outer.chain(&self.local), self.regions)?
.into_frame())
}
- pub fn layout_text(&mut self, node: &TextNode) -> SourceResult<()> {
- let text = node.text();
- let span = node.span();
+ pub fn layout_text(&mut self, elem: &TextElem) -> SourceResult<()> {
+ let text = elem.text();
+ let span = elem.span();
let mut chars = text.chars();
if let Some(glyph) = chars
.next()
@@ -160,7 +160,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
style = style.with_italic(false);
}
let text: EcoString = text.chars().map(|c| style.styled_char(c)).collect();
- let frame = self.layout_content(&TextNode::packed(text).spanned(span))?;
+ let frame = self.layout_content(&TextElem::packed(text).spanned(span))?;
self.push(
FrameFragment::new(self, frame)
.with_class(MathClass::Alphabetic)
@@ -172,21 +172,21 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
}
pub fn styles(&self) -> StyleChain {
- self.outer.chain(&self.map)
+ self.outer.chain(&self.local)
}
pub fn style(&mut self, style: MathStyle) {
self.style_stack.push((self.style, self.size));
- let base_size = TextNode::size_in(self.styles()) / self.style.size.factor(self);
+ let base_size = TextElem::size_in(self.styles()) / self.style.size.factor(self);
self.size = base_size * style.size.factor(self);
- self.map.set(TextNode::set_size(TextSize(self.size.into())));
- self.map
- .set(TextNode::set_style(if style.italic == Smart::Custom(true) {
+ self.local.set(TextElem::set_size(TextSize(self.size.into())));
+ self.local
+ .set(TextElem::set_style(if style.italic == Smart::Custom(true) {
FontStyle::Italic
} else {
FontStyle::Normal
}));
- self.map.set(TextNode::set_weight(if style.bold {
+ self.local.set(TextElem::set_weight(if style.bold {
FontWeight::BOLD
} else {
FontWeight::REGULAR
@@ -196,9 +196,9 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn unstyle(&mut self) {
(self.style, self.size) = self.style_stack.pop().unwrap();
- self.map.unset();
- self.map.unset();
- self.map.unset();
+ self.local.unset();
+ self.local.unset();
+ self.local.unset();
}
}
diff --git a/library/src/math/delimited.rs b/library/src/math/delimited.rs
index 2b9ee5ed..3be17eae 100644
--- a/library/src/math/delimited.rs
+++ b/library/src/math/delimited.rs
@@ -16,8 +16,8 @@ pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1);
///
/// Display: Left/Right
/// Category: math
-#[node(LayoutMath)]
-pub struct LrNode {
+#[element(LayoutMath)]
+pub struct LrElem {
/// The size of the brackets, relative to the height of the wrapped content.
///
/// Defaults to `{100%}`.
@@ -29,7 +29,7 @@ pub struct LrNode {
let mut body = Content::empty();
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
if i > 0 {
- body += TextNode::packed(',');
+ body += TextElem::packed(',');
}
body += arg;
}
@@ -38,12 +38,12 @@ pub struct LrNode {
pub body: Content,
}
-impl LayoutMath for LrNode {
+impl LayoutMath for LrElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let mut body = self.body();
- if let Some(node) = body.to::<LrNode>() {
- if node.size(ctx.styles()).is_auto() {
- body = node.body();
+ if let Some(elem) = body.to::<LrElem>() {
+ if elem.size(ctx.styles()).is_auto() {
+ body = elem.body();
}
}
@@ -179,12 +179,11 @@ pub fn norm(
}
fn delimited(body: Content, left: char, right: char) -> Value {
- Value::Content(
- LrNode::new(Content::sequence(vec![
- TextNode::packed(left),
- body,
- TextNode::packed(right),
- ]))
- .pack(),
- )
+ LrElem::new(Content::sequence([
+ TextElem::packed(left),
+ body,
+ TextElem::packed(right),
+ ]))
+ .pack()
+ .into()
}
diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs
index 90bc69b3..f19fb32e 100644
--- a/library/src/math/frac.rs
+++ b/library/src/math/frac.rs
@@ -19,8 +19,8 @@ const FRAC_AROUND: Em = Em::new(0.1);
///
/// Display: Fraction
/// Category: math
-#[node(LayoutMath)]
-pub struct FracNode {
+#[element(LayoutMath)]
+pub struct FracElem {
/// The fraction's numerator.
#[required]
pub num: Content,
@@ -30,7 +30,7 @@ pub struct FracNode {
pub denom: Content,
}
-impl LayoutMath for FracNode {
+impl LayoutMath for FracElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(ctx, &self.num(), &self.denom(), false, self.span())
}
@@ -45,8 +45,8 @@ impl LayoutMath for FracNode {
///
/// Display: Binomial
/// Category: math
-#[node(LayoutMath)]
-pub struct BinomNode {
+#[element(LayoutMath)]
+pub struct BinomElem {
/// The binomial's upper index.
#[required]
pub upper: Content,
@@ -56,7 +56,7 @@ pub struct BinomNode {
pub lower: Content,
}
-impl LayoutMath for BinomNode {
+impl LayoutMath for BinomElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(ctx, &self.upper(), &self.lower(), true, self.span())
}
@@ -132,9 +132,9 @@ fn layout(
} else {
frame.push(
line_pos,
- Element::Shape(
+ FrameItem::Shape(
Geometry::Line(Point::with_x(line_width)).stroked(Stroke {
- paint: TextNode::fill_in(ctx.styles()),
+ paint: TextElem::fill_in(ctx.styles()),
thickness,
}),
span,
diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs
index 87e28555..0d663d3f 100644
--- a/library/src/math/fragment.rs
+++ b/library/src/math/fragment.rs
@@ -181,8 +181,8 @@ impl GlyphFragment {
id,
c,
font: ctx.font.clone(),
- lang: TextNode::lang_in(ctx.styles()),
- fill: TextNode::fill_in(ctx.styles()),
+ lang: TextElem::lang_in(ctx.styles()),
+ fill: TextElem::fill_in(ctx.styles()),
style: ctx.style,
font_size: ctx.size,
width,
@@ -215,7 +215,7 @@ impl GlyphFragment {
}
pub fn to_frame(&self) -> Frame {
- let text = Text {
+ let item = TextItem {
font: self.font.clone(),
size: self.font_size,
fill: self.fill,
@@ -232,7 +232,7 @@ impl GlyphFragment {
let size = Size::new(self.width, self.ascent + self.descent);
let mut frame = Frame::new(size);
frame.set_baseline(self.ascent);
- frame.push(Point::with_y(self.ascent), Element::Text(text));
+ frame.push(Point::with_y(self.ascent), FrameItem::Text(item));
frame
}
}
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index d79c7ca5..8fba10e7 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -16,8 +16,8 @@ const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
///
/// Display: Vector
/// Category: math
-#[node(LayoutMath)]
-pub struct VecNode {
+#[element(LayoutMath)]
+pub struct VecElem {
/// The delimiter to use.
///
/// ```example
@@ -32,7 +32,7 @@ pub struct VecNode {
pub children: Vec<Content>,
}
-impl LayoutMath for VecNode {
+impl LayoutMath for VecElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = self.delim(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Center)?;
@@ -68,8 +68,8 @@ impl LayoutMath for VecNode {
///
/// Display: Matrix
/// Category: math
-#[node(LayoutMath)]
-pub struct MatNode {
+#[element(LayoutMath)]
+pub struct MatElem {
/// The delimiter to use.
///
/// ```example
@@ -114,7 +114,7 @@ pub struct MatNode {
pub rows: Vec<Vec<Content>>,
}
-impl LayoutMath for MatNode {
+impl LayoutMath for MatElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = self.delim(ctx.styles());
let frame = layout_mat_body(ctx, &self.rows())?;
@@ -144,8 +144,8 @@ impl LayoutMath for MatNode {
///
/// Display: Cases
/// Category: math
-#[node(LayoutMath)]
-pub struct CasesNode {
+#[element(LayoutMath)]
+pub struct CasesElem {
/// The delimiter to use.
///
/// ```example
@@ -160,7 +160,7 @@ pub struct CasesNode {
pub children: Vec<Content>,
}
-impl LayoutMath for CasesNode {
+impl LayoutMath for CasesElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = self.delim(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Left)?;
@@ -221,8 +221,8 @@ fn layout_vec_body(
let gap = ROW_GAP.scaled(ctx);
ctx.style(ctx.style.for_denominator());
let mut flat = vec![];
- for element in column {
- flat.push(ctx.layout_row(element)?);
+ for child in column {
+ flat.push(ctx.layout_row(child)?);
}
ctx.unstyle();
Ok(stack(ctx, flat, align, gap, 0))
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 7fb1aadf..cf665203 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -31,69 +31,69 @@ pub use self::underover::*;
use ttf_parser::{GlyphId, Rect};
use typst::eval::{Module, Scope};
use typst::font::{Font, FontWeight};
-use typst::model::{Guard, SequenceNode, StyledNode};
+use typst::model::Guard;
use unicode_math_class::MathClass;
use self::ctx::*;
use self::fragment::*;
use self::row::*;
use self::spacing::*;
-use crate::layout::{HNode, ParNode, Spacing};
+use crate::layout::{HElem, ParElem, Spacing};
use crate::meta::{Count, Counter, CounterUpdate, LocalName, Numbering};
use crate::prelude::*;
use crate::text::{
- families, variant, FontFamily, FontList, LinebreakNode, SpaceNode, TextNode, TextSize,
+ families, variant, FontFamily, FontList, LinebreakElem, SpaceElem, TextElem, TextSize,
};
/// Create a module with all math definitions.
pub fn module() -> Module {
let mut math = Scope::deduplicating();
- math.define("equation", EquationNode::id());
- math.define("text", TextNode::id());
+ math.define("equation", EquationElem::func());
+ math.define("text", TextElem::func());
// Grouping.
- math.define("lr", LrNode::id());
+ math.define("lr", LrElem::func());
math.define("abs", abs);
math.define("norm", norm);
math.define("floor", floor);
math.define("ceil", ceil);
// Attachments and accents.
- math.define("attach", AttachNode::id());
- math.define("scripts", ScriptsNode::id());
- math.define("limits", LimitsNode::id());
- math.define("accent", AccentNode::id());
- math.define("underline", UnderlineNode::id());
- math.define("overline", OverlineNode::id());
- math.define("underbrace", UnderbraceNode::id());
- math.define("overbrace", OverbraceNode::id());
- math.define("underbracket", UnderbracketNode::id());
- math.define("overbracket", OverbracketNode::id());
+ math.define("attach", AttachElem::func());
+ math.define("scripts", ScriptsElem::func());
+ math.define("limits", LimitsElem::func());
+ math.define("accent", AccentElem::func());
+ math.define("underline", UnderlineElem::func());
+ math.define("overline", OverlineElem::func());
+ math.define("underbrace", UnderbraceElem::func());
+ math.define("overbrace", OverbraceElem::func());
+ math.define("underbracket", UnderbracketElem::func());
+ math.define("overbracket", OverbracketElem::func());
// Fractions and matrix-likes.
- math.define("frac", FracNode::id());
- math.define("binom", BinomNode::id());
- math.define("vec", VecNode::id());
- math.define("mat", MatNode::id());
- math.define("cases", CasesNode::id());
+ math.define("frac", FracElem::func());
+ math.define("binom", BinomElem::func());
+ math.define("vec", VecElem::func());
+ math.define("mat", MatElem::func());
+ math.define("cases", CasesElem::func());
// Roots.
- math.define("sqrt", SqrtNode::id());
- math.define("root", RootNode::id());
+ math.define("sqrt", sqrt);
+ math.define("root", RootElem::func());
// Styles.
- math.define("upright", UprightNode::id());
- math.define("bold", BoldNode::id());
- math.define("italic", ItalicNode::id());
- math.define("serif", SerifNode::id());
- math.define("sans", SansNode::id());
- math.define("cal", CalNode::id());
- math.define("frak", FrakNode::id());
- math.define("mono", MonoNode::id());
- math.define("bb", BbNode::id());
+ math.define("upright", upright);
+ math.define("bold", bold);
+ math.define("italic", italic);
+ math.define("serif", serif);
+ math.define("sans", sans);
+ math.define("cal", cal);
+ math.define("frak", frak);
+ math.define("mono", mono);
+ math.define("bb", bb);
// Text operators.
- math.define("op", OpNode::id());
+ math.define("op", OpElem::func());
op::define(&mut math);
// Spacings.
@@ -133,8 +133,8 @@ pub fn module() -> Module {
///
/// Display: Equation
/// Category: math
-#[node(Locatable, Synthesize, Show, Finalize, Layout, LayoutMath, Count, LocalName)]
-pub struct EquationNode {
+#[element(Locatable, Synthesize, Show, Finalize, Layout, LayoutMath, Count, LocalName)]
+pub struct EquationElem {
/// Whether the equation is displayed as a separate block.
#[default(false)]
pub block: bool,
@@ -157,16 +157,16 @@ pub struct EquationNode {
pub body: Content,
}
-impl Synthesize for EquationNode {
+impl Synthesize for EquationElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_block(self.block(styles));
self.push_numbering(self.numbering(styles));
}
}
-impl Show for EquationNode {
+impl Show for EquationElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- let mut realized = self.clone().pack().guarded(Guard::Base(NodeId::of::<Self>()));
+ let mut realized = self.clone().pack().guarded(Guard::Base(Self::func()));
if self.block(styles) {
realized = realized.aligned(Axes::with_x(Some(Align::Center.into())))
}
@@ -174,17 +174,17 @@ impl Show for EquationNode {
}
}
-impl Finalize for EquationNode {
+impl Finalize for EquationElem {
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
realized
- .styled(TextNode::set_weight(FontWeight::from_number(450)))
- .styled(TextNode::set_font(FontList(vec![FontFamily::new(
+ .styled(TextElem::set_weight(FontWeight::from_number(450)))
+ .styled(TextElem::set_font(FontList(vec![FontFamily::new(
"New Computer Modern Math",
)])))
}
}
-impl Layout for EquationNode {
+impl Layout for EquationElem {
fn layout(
&self,
vt: &mut Vt,
@@ -215,7 +215,7 @@ impl Layout for EquationNode {
if block {
if let Some(numbering) = self.numbering(styles) {
let pod = Regions::one(regions.base(), Axes::splat(false));
- let counter = Counter::of(Self::id())
+ let counter = Counter::of(Self::func())
.display(numbering, false)
.layout(vt, styles, pod)?
.into_frame();
@@ -230,7 +230,7 @@ impl Layout for EquationNode {
let height = frame.height().max(counter.height());
frame.resize(Size::new(width, height), Align::CENTER_HORIZON);
- let x = if TextNode::dir_in(styles).is_positive() {
+ let x = if TextElem::dir_in(styles).is_positive() {
frame.width() - counter.width()
} else {
Abs::zero()
@@ -240,10 +240,10 @@ impl Layout for EquationNode {
frame.push_frame(Point::new(x, y), counter)
}
} else {
- let slack = ParNode::leading_in(styles) * 0.7;
- let top_edge = TextNode::top_edge_in(styles).resolve(styles, font.metrics());
+ let slack = ParElem::leading_in(styles) * 0.7;
+ let top_edge = TextElem::top_edge_in(styles).resolve(styles, font.metrics());
let bottom_edge =
- -TextNode::bottom_edge_in(styles).resolve(styles, font.metrics());
+ -TextElem::bottom_edge_in(styles).resolve(styles, font.metrics());
let ascent = top_edge.max(frame.ascent() - slack);
let descent = bottom_edge.max(frame.descent() - slack);
@@ -255,7 +255,7 @@ impl Layout for EquationNode {
}
}
-impl Count for EquationNode {
+impl Count for EquationElem {
fn update(&self) -> Option<CounterUpdate> {
(self.block(StyleChain::default())
&& self.numbering(StyleChain::default()).is_some())
@@ -263,7 +263,7 @@ impl Count for EquationNode {
}
}
-impl LocalName for EquationNode {
+impl LocalName for EquationElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Gleichung",
@@ -276,7 +276,7 @@ pub trait LayoutMath {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()>;
}
-impl LayoutMath for EquationNode {
+impl LayoutMath for EquationElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
self.body().layout_math(ctx)
}
@@ -284,45 +284,44 @@ impl LayoutMath for EquationNode {
impl LayoutMath for Content {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- if let Some(node) = self.to::<SequenceNode>() {
- for child in node.children() {
+ if let Some(children) = self.to_sequence() {
+ for child in children {
child.layout_math(ctx)?;
}
return Ok(());
}
- if let Some(styled) = self.to::<StyledNode>() {
- let map = styled.styles();
- if TextNode::font_in(ctx.styles().chain(&map))
- != TextNode::font_in(ctx.styles())
+ if let Some((elem, styles)) = self.to_styled() {
+ if TextElem::font_in(ctx.styles().chain(&styles))
+ != TextElem::font_in(ctx.styles())
{
let frame = ctx.layout_content(self)?;
ctx.push(FrameFragment::new(ctx, frame).with_spaced(true));
return Ok(());
}
- let prev_map = std::mem::replace(&mut ctx.map, map);
+ let prev_map = std::mem::replace(&mut ctx.local, styles.clone());
let prev_size = ctx.size;
- ctx.map.apply(prev_map.clone());
- ctx.size = TextNode::size_in(ctx.styles());
- styled.body().layout_math(ctx)?;
+ ctx.local.apply(prev_map.clone());
+ ctx.size = TextElem::size_in(ctx.styles());
+ elem.layout_math(ctx)?;
ctx.size = prev_size;
- ctx.map = prev_map;
+ ctx.local = prev_map;
return Ok(());
}
- if self.is::<SpaceNode>() {
+ if self.is::<SpaceElem>() {
ctx.push(MathFragment::Space(ctx.space_width.scaled(ctx)));
return Ok(());
}
- if self.is::<LinebreakNode>() {
+ if self.is::<LinebreakElem>() {
ctx.push(MathFragment::Linebreak);
return Ok(());
}
- if let Some(node) = self.to::<HNode>() {
- if let Spacing::Rel(rel) = node.amount() {
+ if let Some(elem) = self.to::<HElem>() {
+ if let Spacing::Rel(rel) = elem.amount() {
if rel.rel.is_zero() {
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
}
@@ -330,13 +329,13 @@ impl LayoutMath for Content {
return Ok(());
}
- if let Some(node) = self.to::<TextNode>() {
- ctx.layout_text(node)?;
+ if let Some(elem) = self.to::<TextElem>() {
+ ctx.layout_text(elem)?;
return Ok(());
}
- if let Some(node) = self.with::<dyn LayoutMath>() {
- return node.layout_math(ctx);
+ if let Some(elem) = self.with::<dyn LayoutMath>() {
+ return elem.layout_math(ctx);
}
let mut frame = ctx.layout_content(self)?;
diff --git a/library/src/math/op.rs b/library/src/math/op.rs
index dae43c3a..e8db0c5d 100644
--- a/library/src/math/op.rs
+++ b/library/src/math/op.rs
@@ -20,8 +20,8 @@ use super::*;
///
/// Display: Text Operator
/// Category: math
-#[node(LayoutMath)]
-pub struct OpNode {
+#[element(LayoutMath)]
+pub struct OpElem {
/// The operator's text.
#[required]
pub text: EcoString,
@@ -33,9 +33,9 @@ pub struct OpNode {
pub limits: bool,
}
-impl LayoutMath for OpNode {
+impl LayoutMath for OpElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let frame = ctx.layout_content(&TextNode::packed(self.text()))?;
+ let frame = ctx.layout_content(&TextElem::packed(self.text()))?;
ctx.push(
FrameFragment::new(ctx, frame)
.with_class(MathClass::Large)
@@ -50,14 +50,14 @@ macro_rules! ops {
pub(super) fn define(math: &mut Scope) {
$(math.define(
stringify!($name),
- OpNode::new(ops!(@name $name $(: $value)?).into())
+ OpElem::new(ops!(@name $name $(: $value)?).into())
.with_limits(ops!(@limit $($tts)*))
.pack()
);)*
let dif = |d| {
- HNode::new(THIN.into()).pack()
- + UprightNode::new(TextNode::packed(d)).pack()
+ HElem::new(THIN.into()).pack()
+ + MathStyleElem::new(TextElem::packed(d)).with_italic(Some(false)).pack()
};
math.define("dif", dif('d'));
math.define("Dif", dif('D'));
diff --git a/library/src/math/root.rs b/library/src/math/root.rs
index b4756b9d..037c6ce7 100644
--- a/library/src/math/root.rs
+++ b/library/src/math/root.rs
@@ -9,17 +9,13 @@ use super::*;
///
/// Display: Square Root
/// Category: math
-#[node(LayoutMath)]
-pub struct SqrtNode {
+/// Returns: content
+#[func]
+pub fn sqrt(
/// The expression to take the square root of.
- #[required]
- pub radicand: Content,
-}
-
-impl LayoutMath for SqrtNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, None, &self.radicand(), self.span())
- }
+ radicand: Content,
+) -> Value {
+ RootElem::new(radicand).pack().into()
}
/// A general root.
@@ -31,20 +27,20 @@ impl LayoutMath for SqrtNode {
///
/// Display: Root
/// Category: math
-#[node(LayoutMath)]
-pub struct RootNode {
+#[element(LayoutMath)]
+pub struct RootElem {
/// Which root of the radicand to take.
- #[required]
- index: Content,
+ #[positional]
+ index: Option<Content>,
/// The expression to take the root of.
#[required]
radicand: Content,
}
-impl LayoutMath for RootNode {
+impl LayoutMath for RootElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, Some(&self.index()), &self.radicand(), self.span())
+ layout(ctx, self.index(ctx.styles()).as_ref(), &self.radicand(), self.span())
}
}
@@ -88,7 +84,7 @@ fn layout(
// Layout the index.
// Script-script style looks too small, we use Script style instead.
ctx.style(ctx.style.with_size(MathSize::Script));
- let index = index.map(|node| ctx.layout_frame(node)).transpose()?;
+ let index = index.map(|elem| ctx.layout_frame(elem)).transpose()?;
ctx.unstyle();
let gap = gap.max((sqrt.height() - radicand.height() - thickness) / 2.0);
@@ -124,9 +120,9 @@ fn layout(
frame.push_frame(sqrt_pos, sqrt);
frame.push(
line_pos,
- Element::Shape(
+ FrameItem::Shape(
Geometry::Line(Point::with_x(radicand.width()))
- .stroked(Stroke { paint: TextNode::fill_in(ctx.styles()), thickness }),
+ .stroked(Stroke { paint: TextElem::fill_in(ctx.styles()), thickness }),
span,
),
);
@@ -139,15 +135,15 @@ fn layout(
/// Select a precomposed radical, if the font has it.
fn precomposed(ctx: &MathContext, index: Option<&Content>, target: Abs) -> Option<Frame> {
- let node = index?.to::<TextNode>()?;
- let c = match node.text().as_str() {
+ let elem = index?.to::<TextElem>()?;
+ let c = match elem.text().as_str() {
"3" => '∛',
"4" => '∜',
_ => return None,
};
ctx.ttf.glyph_index(c)?;
- let glyph = GlyphFragment::new(ctx, c, node.span());
+ let glyph = GlyphFragment::new(ctx, c, elem.span());
let variant = glyph.stretch_vertical(ctx, target, Abs::zero()).frame;
if variant.height() < target {
return None;
diff --git a/library/src/math/row.rs b/library/src/math/row.rs
index ecb2e31e..67d9eeaf 100644
--- a/library/src/math/row.rs
+++ b/library/src/math/row.rs
@@ -1,4 +1,4 @@
-use crate::layout::AlignNode;
+use crate::layout::AlignElem;
use super::*;
@@ -103,7 +103,7 @@ impl MathRow {
pub fn to_frame(self, ctx: &MathContext) -> Frame {
let styles = ctx.styles();
- let align = AlignNode::alignment_in(styles).x.resolve(styles);
+ let align = AlignElem::alignment_in(styles).x.resolve(styles);
self.to_aligned_frame(ctx, &[], align)
}
@@ -124,7 +124,7 @@ impl MathRow {
if self.iter().any(|frag| matches!(frag, MathFragment::Linebreak)) {
let fragments: Vec<_> = std::mem::take(&mut self.0);
let leading = if ctx.style.size >= MathSize::Text {
- ParNode::leading_in(ctx.styles())
+ ParElem::leading_in(ctx.styles())
} else {
TIGHT_LEADING.scaled(ctx)
};
diff --git a/library/src/math/spacing.rs b/library/src/math/spacing.rs
index e1b9d408..848aca78 100644
--- a/library/src/math/spacing.rs
+++ b/library/src/math/spacing.rs
@@ -7,10 +7,10 @@ pub(super) const QUAD: Em = Em::new(1.0);
/// Hook up all spacings.
pub(super) fn define(math: &mut Scope) {
- math.define("thin", HNode::new(THIN.into()).pack());
- math.define("med", HNode::new(MEDIUM.into()).pack());
- math.define("thick", HNode::new(THICK.into()).pack());
- math.define("quad", HNode::new(QUAD.into()).pack());
+ math.define("thin", HElem::new(THIN.into()).pack());
+ math.define("med", HElem::new(MEDIUM.into()).pack());
+ math.define("thick", HElem::new(THICK.into()).pack());
+ math.define("quad", HElem::new(QUAD.into()).pack());
}
/// Create the spacing between two fragments in a given style.
diff --git a/library/src/math/style.rs b/library/src/math/style.rs
index a3383a0c..7a911a0a 100644
--- a/library/src/math/style.rs
+++ b/library/src/math/style.rs
@@ -9,20 +9,13 @@ use super::*;
///
/// Display: Bold
/// Category: math
-#[node(LayoutMath)]
-pub struct BoldNode {
+/// Returns: content
+#[func]
+pub fn bold(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for BoldNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_bold(true));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body).with_bold(Some(true)).pack().into()
}
/// Upright (non-italic) font style in math.
@@ -34,20 +27,13 @@ impl LayoutMath for BoldNode {
///
/// Display: Upright
/// Category: math
-#[node(LayoutMath)]
-pub struct UprightNode {
+/// Returns: content
+#[func]
+pub fn upright(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for UprightNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_italic(false));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body).with_italic(Some(false)).pack().into()
}
/// Italic font style in math.
@@ -56,42 +42,30 @@ impl LayoutMath for UprightNode {
///
/// Display: Italic
/// Category: math
-#[node(LayoutMath)]
-pub struct ItalicNode {
+/// Returns: content
+#[func]
+pub fn italic(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for ItalicNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_italic(true));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body).with_italic(Some(true)).pack().into()
}
-
/// Serif (roman) font style in math.
///
/// This is already the default.
///
/// Display: Serif
/// Category: math
-#[node(LayoutMath)]
-pub struct SerifNode {
+/// Returns: content
+#[func]
+pub fn serif(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for SerifNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Serif));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Serif))
+ .pack()
+ .into()
}
/// Sans-serif font style in math.
@@ -103,20 +77,16 @@ impl LayoutMath for SerifNode {
///
/// Display: Sans-serif
/// Category: math
-#[node(LayoutMath)]
-pub struct SansNode {
+/// Returns: content
+#[func]
+pub fn sans(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for SansNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Sans));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Sans))
+ .pack()
+ .into()
}
/// Calligraphic font style in math.
@@ -128,20 +98,16 @@ impl LayoutMath for SansNode {
///
/// Display: Calligraphic
/// Category: math
-#[node(LayoutMath)]
-pub struct CalNode {
+/// Returns: content
+#[func]
+pub fn cal(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for CalNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Cal));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Cal))
+ .pack()
+ .into()
}
/// Fraktur font style in math.
@@ -153,20 +119,16 @@ impl LayoutMath for CalNode {
///
/// Display: Fraktur
/// Category: math
-#[node(LayoutMath)]
-pub struct FrakNode {
+/// Returns: content
+#[func]
+pub fn frak(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for FrakNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Frak));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Frak))
+ .pack()
+ .into()
}
/// Monospace font style in math.
@@ -178,20 +140,16 @@ impl LayoutMath for FrakNode {
///
/// Display: Monospace
/// Category: math
-#[node(LayoutMath)]
-pub struct MonoNode {
+/// Returns: content
+#[func]
+pub fn mono(
/// The content to style.
- #[required]
- pub body: Content,
-}
-
-impl LayoutMath for MonoNode {
- fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Mono));
- self.body().layout_math(ctx)?;
- ctx.unstyle();
- Ok(())
- }
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Mono))
+ .pack()
+ .into()
}
/// Blackboard bold (double-struck) font style in math.
@@ -208,16 +166,51 @@ impl LayoutMath for MonoNode {
///
/// Display: Blackboard Bold
/// Category: math
-#[node(LayoutMath)]
-pub struct BbNode {
+/// Returns: content
+#[func]
+pub fn bb(
+ /// The content to style.
+ body: Content,
+) -> Value {
+ MathStyleElem::new(body)
+ .with_variant(Some(MathVariant::Bb))
+ .pack()
+ .into()
+}
+
+/// A font variant in math.
+///
+/// Display: Bold
+/// Category: math
+#[element(LayoutMath)]
+pub struct MathStyleElem {
/// The content to style.
#[required]
pub body: Content,
+
+ /// The variant to select.
+ pub variant: Option<MathVariant>,
+
+ /// Whether to use bold glyphs.
+ pub bold: Option<bool>,
+
+ /// Whether to use italic glyphs.
+ pub italic: Option<bool>,
}
-impl LayoutMath for BbNode {
+impl LayoutMath for MathStyleElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- ctx.style(ctx.style.with_variant(MathVariant::Bb));
+ let mut style = ctx.style;
+ if let Some(variant) = self.variant(StyleChain::default()) {
+ style = style.with_variant(variant);
+ }
+ if let Some(bold) = self.bold(StyleChain::default()) {
+ style = style.with_bold(bold);
+ }
+ if let Some(italic) = self.italic(StyleChain::default()) {
+ style = style.with_italic(italic);
+ }
+ ctx.style(style);
self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
@@ -324,7 +317,7 @@ impl MathSize {
}
/// A mathematical style variant, as defined by Unicode.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Cast)]
pub enum MathVariant {
Serif,
Sans,
diff --git a/library/src/math/underover.rs b/library/src/math/underover.rs
index cfbb30b6..654da354 100644
--- a/library/src/math/underover.rs
+++ b/library/src/math/underover.rs
@@ -13,14 +13,14 @@ const BRACKET_GAP: Em = Em::new(0.25);
///
/// Display: Underline
/// Category: math
-#[node(LayoutMath)]
-pub struct UnderlineNode {
+#[element(LayoutMath)]
+pub struct UnderlineElem {
/// The content above the line.
#[required]
pub body: Content,
}
-impl LayoutMath for UnderlineNode {
+impl LayoutMath for UnderlineElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(ctx, &self.body(), &None, '\u{305}', LINE_GAP, false, self.span())
}
@@ -35,14 +35,14 @@ impl LayoutMath for UnderlineNode {
///
/// Display: Overline
/// Category: math
-#[node(LayoutMath)]
-pub struct OverlineNode {
+#[element(LayoutMath)]
+pub struct OverlineElem {
/// The content below the line.
#[required]
pub body: Content,
}
-impl LayoutMath for OverlineNode {
+impl LayoutMath for OverlineElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(ctx, &self.body(), &None, '\u{332}', LINE_GAP, true, self.span())
}
@@ -57,8 +57,8 @@ impl LayoutMath for OverlineNode {
///
/// Display: Underbrace
/// Category: math
-#[node(LayoutMath)]
-pub struct UnderbraceNode {
+#[element(LayoutMath)]
+pub struct UnderbraceElem {
/// The content above the brace.
#[required]
pub body: Content,
@@ -68,7 +68,7 @@ pub struct UnderbraceNode {
pub annotation: Option<Content>,
}
-impl LayoutMath for UnderbraceNode {
+impl LayoutMath for UnderbraceElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(
ctx,
@@ -91,8 +91,8 @@ impl LayoutMath for UnderbraceNode {
///
/// Display: Overbrace
/// Category: math
-#[node(LayoutMath)]
-pub struct OverbraceNode {
+#[element(LayoutMath)]
+pub struct OverbraceElem {
/// The content below the brace.
#[required]
pub body: Content,
@@ -102,7 +102,7 @@ pub struct OverbraceNode {
pub annotation: Option<Content>,
}
-impl LayoutMath for OverbraceNode {
+impl LayoutMath for OverbraceElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(
ctx,
@@ -125,8 +125,8 @@ impl LayoutMath for OverbraceNode {
///
/// Display: Underbracket
/// Category: math
-#[node(LayoutMath)]
-pub struct UnderbracketNode {
+#[element(LayoutMath)]
+pub struct UnderbracketElem {
/// The content above the bracket.
#[required]
pub body: Content,
@@ -136,7 +136,7 @@ pub struct UnderbracketNode {
pub annotation: Option<Content>,
}
-impl LayoutMath for UnderbracketNode {
+impl LayoutMath for UnderbracketElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(
ctx,
@@ -159,8 +159,8 @@ impl LayoutMath for UnderbracketNode {
///
/// Display: Overbracket
/// Category: math
-#[node(LayoutMath)]
-pub struct OverbracketNode {
+#[element(LayoutMath)]
+pub struct OverbracketElem {
/// The content below the bracket.
#[required]
pub body: Content,
@@ -170,7 +170,7 @@ pub struct OverbracketNode {
pub annotation: Option<Content>,
}
-impl LayoutMath for OverbracketNode {
+impl LayoutMath for OverbracketElem {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
layout(
ctx,
diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs
index 64ac1f8e..5f244a8f 100644
--- a/library/src/meta/bibliography.rs
+++ b/library/src/meta/bibliography.rs
@@ -8,18 +8,18 @@ use hayagriva::io::{BibLaTeXError, YamlBibliographyError};
use hayagriva::style::{self, Brackets, Citation, Database, DisplayString, Formatting};
use hayagriva::Entry;
-use super::{LocalName, RefNode};
-use crate::layout::{BlockNode, GridNode, ParNode, Sizing, TrackSizings, VNode};
-use crate::meta::HeadingNode;
+use super::{LocalName, RefElem};
+use crate::layout::{BlockElem, GridElem, ParElem, Sizing, TrackSizings, VElem};
+use crate::meta::HeadingElem;
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
/// A bibliography / reference listing.
///
/// Display: Bibliography
/// Category: meta
-#[node(Locatable, Synthesize, Show, LocalName)]
-pub struct BibliographyNode {
+#[element(Locatable, Synthesize, Show, LocalName)]
+pub struct BibliographyElem {
/// Path to a Hayagriva `.yml` or BibLaTeX `.bib` file.
#[required]
#[parse(
@@ -45,11 +45,11 @@ pub struct BibliographyNode {
pub style: BibliographyStyle,
}
-impl BibliographyNode {
+impl BibliographyElem {
/// Find the document's bibliography.
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
- let mut iter = introspector.query(Selector::node::<Self>()).into_iter();
- let Some(node) = iter.next() else {
+ let mut iter = introspector.query(Self::func().select()).into_iter();
+ let Some(elem) = iter.next() else {
return Err("the document does not contain a bibliography".into());
};
@@ -57,15 +57,15 @@ impl BibliographyNode {
Err("multiple bibliographies are not supported")?;
}
- Ok(node.to::<Self>().unwrap().clone())
+ Ok(elem.to::<Self>().unwrap().clone())
}
/// Whether the bibliography contains the given key.
pub fn has(vt: &Vt, key: &str) -> bool {
vt.introspector
- .query(Selector::node::<Self>())
+ .query(Self::func().select())
.into_iter()
- .flat_map(|node| load(vt.world, &node.to::<Self>().unwrap().path()))
+ .flat_map(|elem| load(vt.world, &elem.to::<Self>().unwrap().path()))
.flatten()
.any(|entry| entry.key() == key)
}
@@ -76,7 +76,7 @@ impl BibliographyNode {
introspector: Tracked<Introspector>,
) -> Vec<(EcoString, Option<EcoString>)> {
Self::find(introspector)
- .and_then(|node| load(world, &node.path()))
+ .and_then(|elem| load(world, &elem.path()))
.into_iter()
.flatten()
.map(|entry| {
@@ -89,13 +89,13 @@ impl BibliographyNode {
}
}
-impl Synthesize for BibliographyNode {
+impl Synthesize for BibliographyElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_style(self.style(styles));
}
}
-impl Show for BibliographyNode {
+impl Show for BibliographyElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
const COLUMN_GUTTER: Em = Em::new(0.65);
const INDENT: Em = Em::new(1.5);
@@ -103,12 +103,12 @@ impl Show for BibliographyNode {
let mut seq = vec![];
if let Some(title) = self.title(styles) {
let title = title.clone().unwrap_or_else(|| {
- TextNode::packed(self.local_name(TextNode::lang_in(styles)))
+ TextElem::packed(self.local_name(TextElem::lang_in(styles)))
.spanned(self.span())
});
seq.push(
- HeadingNode::new(title)
+ HeadingElem::new(title)
.with_level(NonZeroUsize::ONE)
.with_numbering(None)
.pack(),
@@ -121,7 +121,7 @@ impl Show for BibliographyNode {
let works = Works::new(vt).at(self.span())?;
- let row_gutter = BlockNode::below_in(styles).amount();
+ let row_gutter = BlockElem::below_in(styles).amount();
if works.references.iter().any(|(prefix, _)| prefix.is_some()) {
let mut cells = vec![];
for (prefix, reference) in &works.references {
@@ -129,9 +129,9 @@ impl Show for BibliographyNode {
cells.push(reference.clone());
}
- seq.push(VNode::new(row_gutter).with_weakness(3).pack());
+ seq.push(VElem::new(row_gutter).with_weakness(3).pack());
seq.push(
- GridNode::new(cells)
+ GridElem::new(cells)
.with_columns(TrackSizings(vec![Sizing::Auto; 2]))
.with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()]))
.with_row_gutter(TrackSizings(vec![row_gutter.into()]))
@@ -140,13 +140,13 @@ impl Show for BibliographyNode {
} else {
let mut entries = vec![];
for (_, reference) in &works.references {
- entries.push(VNode::new(row_gutter).with_weakness(3).pack());
+ entries.push(VElem::new(row_gutter).with_weakness(3).pack());
entries.push(reference.clone());
}
seq.push(
Content::sequence(entries)
- .styled(ParNode::set_hanging_indent(INDENT.into())),
+ .styled(ParElem::set_hanging_indent(INDENT.into())),
);
}
@@ -154,7 +154,7 @@ impl Show for BibliographyNode {
}
}
-impl LocalName for BibliographyNode {
+impl LocalName for BibliographyElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Bibliographie",
@@ -196,8 +196,8 @@ impl BibliographyStyle {
///
/// Display: Citation
/// Category: meta
-#[node(Locatable, Synthesize, Show)]
-pub struct CiteNode {
+#[element(Locatable, Synthesize, Show)]
+pub struct CiteElem {
/// The citation key.
#[variadic]
pub keys: Vec<EcoString>,
@@ -217,7 +217,7 @@ pub struct CiteNode {
pub style: Smart<CitationStyle>,
}
-impl Synthesize for CiteNode {
+impl Synthesize for CiteElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_supplement(self.supplement(styles));
self.push_brackets(self.brackets(styles));
@@ -225,17 +225,17 @@ impl Synthesize for CiteNode {
}
}
-impl Show for CiteNode {
+impl Show for CiteElem {
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
let works = Works::new(vt).at(self.span())?;
- let id = self.0.stable_id().unwrap();
+ let location = self.0.location().unwrap();
works
.citations
- .get(&id)
+ .get(&location)
.cloned()
.flatten()
.ok_or("bibliography does not contain this key")
@@ -264,24 +264,24 @@ pub enum CitationStyle {
/// Fully formatted citations and references.
#[derive(Default)]
struct Works {
- citations: HashMap<StableId, Option<Content>>,
+ citations: HashMap<Location, Option<Content>>,
references: Vec<(Option<Content>, Content)>,
}
impl Works {
/// Prepare all things need to cite a work or format a bibliography.
fn new(vt: &Vt) -> StrResult<Arc<Self>> {
- let bibliography = BibliographyNode::find(vt.introspector)?;
+ let bibliography = BibliographyElem::find(vt.introspector)?;
let citations = vt
.introspector
.query(Selector::Any(eco_vec![
- Selector::node::<RefNode>(),
- Selector::node::<CiteNode>(),
+ RefElem::func().select(),
+ CiteElem::func().select(),
]))
.into_iter()
- .map(|node| match node.to::<RefNode>() {
+ .map(|elem| match elem.to::<RefElem>() {
Some(reference) => reference.to_citation(StyleChain::default()),
- _ => node.to::<CiteNode>().unwrap().clone(),
+ _ => elem.to::<CiteElem>().unwrap().clone(),
})
.collect();
Ok(create(vt.world, bibliography, citations))
@@ -292,19 +292,19 @@ impl Works {
#[comemo::memoize]
fn create(
world: Tracked<dyn World>,
- bibliography: BibliographyNode,
- citations: Vec<CiteNode>,
+ bibliography: BibliographyElem,
+ citations: Vec<CiteElem>,
) -> Arc<Works> {
let span = bibliography.span();
let entries = load(world, &bibliography.path()).unwrap();
let style = bibliography.style(StyleChain::default());
- let bib_id = bibliography.0.stable_id().unwrap();
- let ref_id = |target: &Entry| {
+ let bib_location = bibliography.0.location().unwrap();
+ let ref_location = |target: &Entry| {
let i = entries
.iter()
.position(|entry| entry.key() == target.key())
.unwrap_or_default();
- bib_id.variant(i)
+ bib_location.variant(i)
};
let mut db = Database::new();
@@ -312,7 +312,7 @@ fn create(
let mut preliminary = vec![];
for citation in citations {
- let cite_id = citation.0.stable_id().unwrap();
+ let cite_id = citation.0.location().unwrap();
let entries = citation
.keys()
.into_iter()
@@ -333,8 +333,8 @@ fn create(
let citations = preliminary
.into_iter()
.map(|(citation, cited)| {
- let id = citation.0.stable_id().unwrap();
- let Some(cited) = cited else { return (id, None) };
+ let location = citation.0.location().unwrap();
+ let Some(cited) = cited else { return (location, None) };
let mut supplement = citation.supplement(StyleChain::default());
let brackets = citation.brackets(StyleChain::default());
@@ -376,27 +376,27 @@ fn create(
}
if i > 0 {
- content += TextNode::packed(",\u{a0}");
+ content += TextElem::packed(",\u{a0}");
}
// Format and link to the reference entry.
content += format_display_string(&display, supplement, citation.span())
- .linked(Link::Node(ref_id(entry)));
+ .linked(Destination::Location(ref_location(entry)));
}
if brackets && len > 1 {
content = match citation_style.brackets() {
Brackets::None => content,
Brackets::Round => {
- TextNode::packed('(') + content + TextNode::packed(')')
+ TextElem::packed('(') + content + TextElem::packed(')')
}
Brackets::Square => {
- TextNode::packed('[') + content + TextNode::packed(']')
+ TextElem::packed('[') + content + TextElem::packed(']')
}
};
}
- (id, Some(content))
+ (location, Some(content))
})
.collect();
@@ -414,15 +414,15 @@ fn create(
// Make link from citation to here work.
let backlink = {
let mut content = Content::empty();
- content.set_stable_id(ref_id(&reference.entry));
- MetaNode::set_data(vec![Meta::Node(content)])
+ content.set_location(ref_location(&reference.entry));
+ MetaElem::set_data(vec![Meta::Elem(content)])
};
let prefix = reference.prefix.map(|prefix| {
// Format and link to first citation.
let bracketed = prefix.with_default_brackets(&*citation_style);
format_display_string(&bracketed, None, span)
- .linked(Link::Node(ids[reference.entry.key()]))
+ .linked(Destination::Location(ids[reference.entry.key()]))
.styled(backlink.clone())
});
@@ -510,7 +510,7 @@ fn format_display_string(
let mut content = if segment == SUPPLEMENT && supplement.is_some() {
supplement.take().unwrap_or_default()
} else {
- TextNode::packed(segment).spanned(span)
+ TextElem::packed(segment).spanned(span)
};
for (range, fmt) in &string.formatting {
@@ -522,7 +522,7 @@ fn format_display_string(
Formatting::Bold => content.strong(),
Formatting::Italic => content.emph(),
Formatting::Link(link) => {
- content.linked(Link::Dest(Destination::Url(link.as_str().into())))
+ content.linked(Destination::Url(link.as_str().into()))
}
};
}
diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs
index 9e542847..dbb84812 100644
--- a/library/src/meta/context.rs
+++ b/library/src/meta/context.rs
@@ -10,28 +10,28 @@ pub fn locate(
/// The function to call with the location.
func: Func,
) -> Value {
- LocateNode::new(func).pack().into()
+ LocateElem::new(func).pack().into()
}
/// Executes a `locate` call.
///
/// Display: Styled
/// Category: special
-#[node(Locatable, Show)]
-struct LocateNode {
+#[element(Locatable, Show)]
+struct LocateElem {
/// The function to call with the location.
#[required]
func: Func,
}
-impl Show for LocateNode {
+impl Show for LocateElem {
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
- let id = self.0.stable_id().unwrap();
- Ok(self.func().call_vt(vt, [id.into()])?.display())
+ let location = self.0.location().unwrap();
+ Ok(self.func().call_vt(vt, [location.into()])?.display())
}
}
@@ -45,21 +45,21 @@ pub fn style(
/// The function to call with the styles.
func: Func,
) -> Value {
- StyleNode::new(func).pack().into()
+ StyleElem::new(func).pack().into()
}
/// Executes a style access.
///
/// Display: Style
/// Category: special
-#[node(Show)]
-struct StyleNode {
+#[element(Show)]
+struct StyleElem {
/// The function to call with the styles.
#[required]
func: Func,
}
-impl Show for StyleNode {
+impl Show for StyleElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(self.func().call_vt(vt, [styles.to_map().into()])?.display())
}
diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs
index f033a04f..3cfc2cd5 100644
--- a/library/src/meta/counter.rs
+++ b/library/src/meta/counter.rs
@@ -6,7 +6,7 @@ use smallvec::{smallvec, SmallVec};
use typst::eval::Tracer;
use super::{Numbering, NumberingPattern};
-use crate::layout::PageNode;
+use crate::layout::PageElem;
use crate::prelude::*;
/// Count through pages, elements, and more.
@@ -32,9 +32,9 @@ impl Counter {
Self(key)
}
- /// The counter for the given node.
- pub fn of(id: NodeId) -> Self {
- Self::new(CounterKey::Selector(Selector::Node(id, None)))
+ /// The counter for the given element.
+ pub fn of(func: ElemFunc) -> Self {
+ Self::new(CounterKey::Selector(Selector::Elem(func, None)))
}
/// Call a method on counter.
@@ -69,23 +69,23 @@ impl Counter {
/// Display the current value of the counter.
pub fn display(self, numbering: Numbering, both: bool) -> Content {
- DisplayNode::new(self, numbering, both).pack()
+ DisplayElem::new(self, numbering, both).pack()
}
/// Get the value of the state at the given location.
- pub fn at(&self, vt: &mut Vt, id: StableId) -> SourceResult<CounterState> {
+ pub fn at(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
- let offset = vt.introspector.query_before(self.selector(), id).len();
+ let offset = vt.introspector.query_before(self.selector(), location).len();
let (mut state, page) = sequence[offset].clone();
if self.is_page() {
- let delta = vt.introspector.page(id).get() - page.get();
+ let delta = vt.introspector.page(location).get() - page.get();
state.step(NonZeroUsize::ONE, delta);
}
Ok(state)
}
/// Get the value of the state at the final location.
- pub fn final_(&self, vt: &mut Vt, _: StableId) -> SourceResult<CounterState> {
+ pub fn final_(&self, vt: &mut Vt, _: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
let (mut state, page) = sequence.last().unwrap().clone();
if self.is_page() {
@@ -96,13 +96,13 @@ impl Counter {
}
/// Get the current and final value of the state combined in one state.
- pub fn both(&self, vt: &mut Vt, id: StableId) -> SourceResult<CounterState> {
+ pub fn both(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
- let offset = vt.introspector.query_before(self.selector(), id).len();
+ let offset = vt.introspector.query_before(self.selector(), location).len();
let (mut at_state, at_page) = sequence[offset].clone();
let (mut final_state, final_page) = sequence.last().unwrap().clone();
if self.is_page() {
- let at_delta = vt.introspector.page(id).get() - at_page.get();
+ let at_delta = vt.introspector.page(location).get() - at_page.get();
at_state.step(NonZeroUsize::ONE, at_delta);
let final_delta = vt.introspector.pages().get() - final_page.get();
final_state.step(NonZeroUsize::ONE, final_delta);
@@ -112,7 +112,7 @@ impl Counter {
/// Produce content that performs a state update.
pub fn update(self, update: CounterUpdate) -> Content {
- UpdateNode::new(self, update).pack()
+ UpdateElem::new(self, update).pack()
}
/// Produce the whole sequence of counter states.
@@ -148,11 +148,11 @@ impl Counter {
let mut page = NonZeroUsize::ONE;
let mut stops = eco_vec![(state.clone(), page)];
- for node in introspector.query(self.selector()) {
+ for elem in introspector.query(self.selector()) {
if self.is_page() {
- let id = node.stable_id().unwrap();
+ let location = elem.location().unwrap();
let prev = page;
- page = introspector.page(id);
+ page = introspector.page(location);
let delta = page.get() - prev.get();
if delta > 0 {
@@ -160,9 +160,9 @@ impl Counter {
}
}
- if let Some(update) = match node.to::<UpdateNode>() {
- Some(node) => Some(node.update()),
- None => match node.with::<dyn Count>() {
+ if let Some(update) = match elem.to::<UpdateElem>() {
+ Some(elem) => Some(elem.update()),
+ None => match elem.with::<dyn Count>() {
Some(countable) => countable.update(),
None => Some(CounterUpdate::Step(NonZeroUsize::ONE)),
},
@@ -178,10 +178,8 @@ impl Counter {
/// The selector relevant for this counter's updates.
fn selector(&self) -> Selector {
- let mut selector = Selector::Node(
- NodeId::of::<UpdateNode>(),
- Some(dict! { "counter" => self.clone() }),
- );
+ let mut selector =
+ Selector::Elem(UpdateElem::func(), Some(dict! { "counter" => self.clone() }));
if let CounterKey::Selector(key) = &self.0 {
selector = Selector::Any(eco_vec![selector, key.clone()]);
@@ -224,20 +222,16 @@ cast_from_value! {
CounterKey,
v: Str => Self::Str(v),
label: Label => Self::Selector(Selector::Label(label)),
- func: Func => {
- let Some(id) = func.id() else {
- return Err("this function is not selectable".into());
- };
-
- if id == NodeId::of::<PageNode>() {
+ element: ElemFunc => {
+ if element == PageElem::func() {
return Ok(Self::Page);
}
- if !Content::new(id).can::<dyn Locatable>() {
- Err(eco_format!("cannot count through {}s", id.name))?;
+ if !Content::new(element).can::<dyn Locatable>() {
+ Err(eco_format!("cannot count through {}s", element.name()))?;
}
- Self::Selector(Selector::Node(id, None))
+ Self::Selector(Selector::Elem(element, None))
}
}
@@ -274,9 +268,9 @@ cast_from_value! {
v: Func => Self::Func(v),
}
-/// Nodes that have special counting behaviour.
+/// Elements that have special counting behaviour.
pub trait Count {
- /// Get the counter update for this node.
+ /// Get the counter update for this element.
fn update(&self) -> Option<CounterUpdate>;
}
@@ -342,8 +336,8 @@ cast_to_value! {
///
/// Display: State
/// Category: special
-#[node(Locatable, Show)]
-struct DisplayNode {
+#[element(Locatable, Show)]
+struct DisplayElem {
/// The counter.
#[required]
counter: Counter,
@@ -357,12 +351,16 @@ struct DisplayNode {
both: bool,
}
-impl Show for DisplayNode {
+impl Show for DisplayElem {
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
- let id = self.0.stable_id().unwrap();
+ let location = self.0.location().unwrap();
let counter = self.counter();
let numbering = self.numbering();
- let state = if self.both() { counter.both(vt, id) } else { counter.at(vt, id) }?;
+ let state = if self.both() {
+ counter.both(vt, location)?
+ } else {
+ counter.at(vt, location)?
+ };
state.display(vt, &numbering)
}
}
@@ -371,8 +369,8 @@ impl Show for DisplayNode {
///
/// Display: State
/// Category: special
-#[node(Locatable, Show)]
-struct UpdateNode {
+#[element(Locatable, Show)]
+struct UpdateElem {
/// The counter.
#[required]
counter: Counter,
@@ -382,7 +380,7 @@ struct UpdateNode {
update: CounterUpdate,
}
-impl Show for UpdateNode {
+impl Show for UpdateElem {
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(Content::empty())
}
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 75e78184..99e5c6b6 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -1,6 +1,4 @@
-use typst::model::StyledNode;
-
-use crate::layout::{LayoutRoot, PageNode};
+use crate::layout::{LayoutRoot, PageElem};
use crate::prelude::*;
/// The root element of a document and its metadata.
@@ -14,8 +12,8 @@ use crate::prelude::*;
///
/// Display: Document
/// Category: meta
-#[node(LayoutRoot)]
-pub struct DocumentNode {
+#[element(LayoutRoot)]
+pub struct DocumentElem {
/// The document's title. This is often rendered as the title of the
/// PDF viewer window.
pub title: Option<EcoString>,
@@ -29,22 +27,20 @@ pub struct DocumentNode {
pub children: Vec<Content>,
}
-impl LayoutRoot for DocumentNode {
+impl LayoutRoot for DocumentElem {
/// Layout the document into a sequence of frames, one per page.
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
let mut pages = vec![];
- for mut child in self.children() {
- let map;
+ for mut child in &self.children() {
let outer = styles;
- let mut styles = outer;
- if let Some(node) = child.to::<StyledNode>() {
- map = node.styles();
- styles = outer.chain(&map);
- child = node.body();
+ let mut styles = styles;
+ if let Some((elem, local)) = child.to_styled() {
+ styles = outer.chain(local);
+ child = elem;
}
- if let Some(page) = child.to::<PageNode>() {
+ if let Some(page) = child.to::<PageElem>() {
let fragment = page.layout(vt, styles)?;
pages.extend(fragment);
} else {
diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs
index 6f9011b8..9251f3ef 100644
--- a/library/src/meta/figure.rs
+++ b/library/src/meta/figure.rs
@@ -1,9 +1,9 @@
use std::str::FromStr;
use super::{Count, Counter, CounterUpdate, LocalName, Numbering, NumberingPattern};
-use crate::layout::{BlockNode, VNode};
+use crate::layout::{BlockElem, VElem};
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
/// A figure with an optional caption.
///
@@ -23,8 +23,8 @@ use crate::text::TextNode;
///
/// Display: Figure
/// Category: meta
-#[node(Locatable, Synthesize, Count, Show, LocalName)]
-pub struct FigureNode {
+#[element(Locatable, Synthesize, Count, Show, LocalName)]
+pub struct FigureElem {
/// The content of the figure. Often, an [image]($func/image).
#[required]
pub body: Content,
@@ -42,32 +42,32 @@ pub struct FigureNode {
pub gap: Length,
}
-impl Synthesize for FigureNode {
+impl Synthesize for FigureElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_numbering(self.numbering(styles));
}
}
-impl Show for FigureNode {
+impl Show for FigureElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body();
if let Some(mut caption) = self.caption(styles) {
if let Some(numbering) = self.numbering(styles) {
- let name = self.local_name(TextNode::lang_in(styles));
- caption = TextNode::packed(eco_format!("{name}\u{a0}"))
- + Counter::of(Self::id())
+ let name = self.local_name(TextElem::lang_in(styles));
+ caption = TextElem::packed(eco_format!("{name}\u{a0}"))
+ + Counter::of(Self::func())
.display(numbering, false)
.spanned(self.span())
- + TextNode::packed(": ")
+ + TextElem::packed(": ")
+ caption;
}
- realized += VNode::weak(self.gap(styles).into()).pack();
+ realized += VElem::weak(self.gap(styles).into()).pack();
realized += caption;
}
- Ok(BlockNode::new()
+ Ok(BlockElem::new()
.with_body(Some(realized))
.with_breakable(false)
.pack()
@@ -75,7 +75,7 @@ impl Show for FigureNode {
}
}
-impl Count for FigureNode {
+impl Count for FigureElem {
fn update(&self) -> Option<CounterUpdate> {
self.numbering(StyleChain::default())
.is_some()
@@ -83,7 +83,7 @@ impl Count for FigureNode {
}
}
-impl LocalName for FigureNode {
+impl LocalName for FigureElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Abbildung",
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index 61605e67..1eaca187 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -1,10 +1,10 @@
use typst::font::FontWeight;
use super::{Counter, CounterUpdate, LocalName, Numbering};
-use crate::layout::{BlockNode, HNode, VNode};
+use crate::layout::{BlockElem, HElem, VElem};
use crate::meta::Count;
use crate::prelude::*;
-use crate::text::{TextNode, TextSize};
+use crate::text::{TextElem, TextSize};
/// A section heading.
///
@@ -41,8 +41,8 @@ use crate::text::{TextNode, TextSize};
///
/// Display: Heading
/// Category: meta
-#[node(Locatable, Synthesize, Count, Show, Finalize, LocalName)]
-pub struct HeadingNode {
+#[element(Locatable, Synthesize, Count, Show, Finalize, LocalName)]
+pub struct HeadingElem {
/// The logical nesting depth of the heading, starting from one.
#[default(NonZeroUsize::ONE)]
pub level: NonZeroUsize,
@@ -79,7 +79,7 @@ pub struct HeadingNode {
pub body: Content,
}
-impl Synthesize for HeadingNode {
+impl Synthesize for HeadingElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_level(self.level(styles));
self.push_numbering(self.numbering(styles));
@@ -87,20 +87,21 @@ impl Synthesize for HeadingNode {
}
}
-impl Show for HeadingNode {
+impl Show for HeadingElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body();
if let Some(numbering) = self.numbering(styles) {
- realized =
- Counter::of(Self::id()).display(numbering, false).spanned(self.span())
- + HNode::new(Em::new(0.3).into()).with_weak(true).pack()
- + realized;
+ realized = Counter::of(Self::func())
+ .display(numbering, false)
+ .spanned(self.span())
+ + HElem::new(Em::new(0.3).into()).with_weak(true).pack()
+ + realized;
}
- Ok(BlockNode::new().with_body(Some(realized)).pack())
+ Ok(BlockElem::new().with_body(Some(realized)).pack())
}
}
-impl Finalize for HeadingNode {
+impl Finalize for HeadingElem {
fn finalize(&self, realized: Content, styles: StyleChain) -> Content {
let level = self.level(styles).get();
let scale = match level {
@@ -113,17 +114,17 @@ impl Finalize for HeadingNode {
let above = Em::new(if level == 1 { 1.8 } else { 1.44 }) / scale;
let below = Em::new(0.75) / scale;
- let mut map = StyleMap::new();
- map.set(TextNode::set_size(TextSize(size.into())));
- map.set(TextNode::set_weight(FontWeight::BOLD));
- map.set(BlockNode::set_above(VNode::block_around(above.into())));
- map.set(BlockNode::set_below(VNode::block_around(below.into())));
- map.set(BlockNode::set_sticky(true));
- realized.styled_with_map(map)
+ let mut styles = Styles::new();
+ styles.set(TextElem::set_size(TextSize(size.into())));
+ styles.set(TextElem::set_weight(FontWeight::BOLD));
+ styles.set(BlockElem::set_above(VElem::block_around(above.into())));
+ styles.set(BlockElem::set_below(VElem::block_around(below.into())));
+ styles.set(BlockElem::set_sticky(true));
+ realized.styled_with_map(styles)
}
}
-impl Count for HeadingNode {
+impl Count for HeadingElem {
fn update(&self) -> Option<CounterUpdate> {
self.numbering(StyleChain::default())
.is_some()
@@ -132,11 +133,11 @@ impl Count for HeadingNode {
}
cast_from_value! {
- HeadingNode,
+ HeadingElem,
v: Content => v.to::<Self>().ok_or("expected heading")?.clone(),
}
-impl LocalName for HeadingNode {
+impl LocalName for HeadingElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Abschnitt",
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index e9b8bcc6..c82a7443 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -1,5 +1,5 @@
use crate::prelude::*;
-use crate::text::{Hyphenate, TextNode};
+use crate::text::{Hyphenate, TextElem};
/// Link to a URL or another location in the document.
///
@@ -25,19 +25,20 @@ use crate::text::{Hyphenate, TextNode};
///
/// Display: Link
/// Category: meta
-#[node(Show, Finalize)]
-pub struct LinkNode {
+#[element(Show, Finalize)]
+pub struct LinkElem {
/// The destination the link points to.
///
- /// - To link to web pages, `dest` should be a valid URL string. If the URL is
- /// in the `mailto:` or `tel:` scheme and the `body` parameter is omitted,
- /// the email address or phone number will be the link's body, without the
- /// scheme.
+ /// - To link to web pages, `dest` should be a valid URL string. If the URL
+ /// is in the `mailto:` or `tel:` scheme and the `body` parameter is
+ /// omitted, the email address or phone number will be the link's body,
+ /// without the scheme.
///
- /// - To link to another part of the document, `dest` must contain a
- /// dictionary with a `page` key of type `integer` and `x` and `y`
- /// coordinates of type `length`. Pages are counted from one, and the
- /// coordinates are relative to the page's top left corner.
+ /// - To link to another part of the document, `dest` can take one of two
+ /// forms: A [`location`]($func/locate) or a dictionary with a `page` key
+ /// of type `integer` and `x` and `y` coordinates of type `length`. Pages
+ /// are counted from one, and the coordinates are relative to the page's
+ /// top left corner.
///
/// ```example
/// #link("mailto:hello@typst.app") \
@@ -45,7 +46,6 @@ pub struct LinkNode {
/// Go to top
/// ]
/// ```
- ///
#[required]
#[parse(
let dest = args.expect::<Destination>("destination")?;
@@ -64,30 +64,30 @@ pub struct LinkNode {
Some(body) => body,
None => body_from_url(url),
},
- Destination::Internal(_) => args.expect("body")?,
+ _ => args.expect("body")?,
})]
pub body: Content,
}
-impl LinkNode {
- /// Create a link node from a URL with its bare text.
+impl LinkElem {
+ /// Create a link element from a URL with its bare text.
pub fn from_url(url: EcoString) -> Self {
let body = body_from_url(&url);
Self::new(Destination::Url(url), body)
}
}
-impl Show for LinkNode {
+impl Show for LinkElem {
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(self.body())
}
}
-impl Finalize for LinkNode {
+impl Finalize for LinkElem {
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
realized
- .linked(Link::Dest(self.dest()))
- .styled(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))))
+ .linked(self.dest())
+ .styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))))
}
}
@@ -97,5 +97,5 @@ fn body_from_url(url: &EcoString) -> Content {
text = text.trim_start_matches(prefix);
}
let shorter = text.len() < url.len();
- TextNode::packed(if shorter { text.into() } else { url.clone() })
+ TextElem::packed(if shorter { text.into() } else { url.clone() })
}
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index a0a23897..002d757e 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -1,7 +1,7 @@
-use super::{Counter, HeadingNode, LocalName};
-use crate::layout::{BoxNode, HNode, HideNode, ParbreakNode, RepeatNode};
+use super::{Counter, HeadingElem, LocalName};
+use crate::layout::{BoxElem, HElem, HideElem, ParbreakElem, RepeatElem};
use crate::prelude::*;
-use crate::text::{LinebreakNode, SpaceNode, TextNode};
+use crate::text::{LinebreakElem, SpaceElem, TextElem};
/// A section outline / table of contents.
///
@@ -22,8 +22,8 @@ use crate::text::{LinebreakNode, SpaceNode, TextNode};
///
/// Display: Outline
/// Category: meta
-#[node(Show, LocalName)]
-pub struct OutlineNode {
+#[element(Show, LocalName)]
+pub struct OutlineElem {
/// The title of the outline.
///
/// - When set to `{auto}`, an appropriate title for the [text
@@ -65,21 +65,21 @@ pub struct OutlineNode {
///
/// = A New Beginning
/// ```
- #[default(Some(RepeatNode::new(TextNode::packed(".")).pack()))]
+ #[default(Some(RepeatElem::new(TextElem::packed(".")).pack()))]
pub fill: Option<Content>,
}
-impl Show for OutlineNode {
+impl Show for OutlineElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- let mut seq = vec![ParbreakNode::new().pack()];
+ let mut seq = vec![ParbreakElem::new().pack()];
if let Some(title) = self.title(styles) {
let title = title.clone().unwrap_or_else(|| {
- TextNode::packed(self.local_name(TextNode::lang_in(styles)))
+ TextElem::packed(self.local_name(TextElem::lang_in(styles)))
.spanned(self.span())
});
seq.push(
- HeadingNode::new(title)
+ HeadingElem::new(title)
.with_level(NonZeroUsize::ONE)
.with_numbering(None)
.with_outlined(false)
@@ -90,15 +90,15 @@ impl Show for OutlineNode {
let indent = self.indent(styles);
let depth = self.depth(styles);
- let mut ancestors: Vec<&HeadingNode> = vec![];
- let nodes = vt.introspector.query(Selector::Node(
- NodeId::of::<HeadingNode>(),
+ let mut ancestors: Vec<&HeadingElem> = vec![];
+ let elems = vt.introspector.query(Selector::Elem(
+ HeadingElem::func(),
Some(dict! { "outlined" => true }),
));
- for node in &nodes {
- let heading = node.to::<HeadingNode>().unwrap();
- let stable_id = heading.0.stable_id().unwrap();
+ for elem in &elems {
+ let heading = elem.to::<HeadingElem>().unwrap();
+ let location = heading.0.location().unwrap();
if !heading.outlined(StyleChain::default()) {
continue;
}
@@ -120,60 +120,60 @@ impl Show for OutlineNode {
let mut hidden = Content::empty();
for ancestor in &ancestors {
if let Some(numbering) = ancestor.numbering(StyleChain::default()) {
- let numbers = Counter::of(HeadingNode::id())
- .at(vt, ancestor.0.stable_id().unwrap())?
+ let numbers = Counter::of(HeadingElem::func())
+ .at(vt, ancestor.0.location().unwrap())?
.display(vt, &numbering)?;
- hidden += numbers + SpaceNode::new().pack();
+ hidden += numbers + SpaceElem::new().pack();
};
}
if !ancestors.is_empty() {
- seq.push(HideNode::new(hidden).pack());
- seq.push(SpaceNode::new().pack());
+ seq.push(HideElem::new(hidden).pack());
+ seq.push(SpaceElem::new().pack());
}
}
// Format the numbering.
let mut start = heading.body();
if let Some(numbering) = heading.numbering(StyleChain::default()) {
- let numbers = Counter::of(HeadingNode::id())
- .at(vt, stable_id)?
+ let numbers = Counter::of(HeadingElem::func())
+ .at(vt, location)?
.display(vt, &numbering)?;
- start = numbers + SpaceNode::new().pack() + start;
+ start = numbers + SpaceElem::new().pack() + start;
};
// Add the numbering and section name.
- seq.push(start.linked(Link::Node(stable_id)));
+ seq.push(start.linked(Destination::Location(location)));
// Add filler symbols between the section name and page number.
if let Some(filler) = self.fill(styles) {
- seq.push(SpaceNode::new().pack());
+ seq.push(SpaceElem::new().pack());
seq.push(
- BoxNode::new()
+ BoxElem::new()
.with_body(Some(filler.clone()))
.with_width(Fr::one().into())
.pack(),
);
- seq.push(SpaceNode::new().pack());
+ seq.push(SpaceElem::new().pack());
} else {
- seq.push(HNode::new(Fr::one().into()).pack());
+ seq.push(HElem::new(Fr::one().into()).pack());
}
// Add the page number and linebreak.
- let page = vt.introspector.page(stable_id);
- let end = TextNode::packed(eco_format!("{page}"));
- seq.push(end.linked(Link::Node(stable_id)));
- seq.push(LinebreakNode::new().pack());
+ let page = vt.introspector.page(location);
+ let end = TextElem::packed(eco_format!("{page}"));
+ seq.push(end.linked(Destination::Location(location)));
+ seq.push(LinebreakElem::new().pack());
ancestors.push(heading);
}
- seq.push(ParbreakNode::new().pack());
+ seq.push(ParbreakElem::new().pack());
Ok(Content::sequence(seq))
}
}
-impl LocalName for OutlineNode {
+impl LocalName for OutlineElem {
fn local_name(&self, lang: Lang) -> &'static str {
match lang {
Lang::GERMAN => "Inhaltsverzeichnis",
diff --git a/library/src/meta/query.rs b/library/src/meta/query.rs
index 23e310fe..94b25cbc 100644
--- a/library/src/meta/query.rs
+++ b/library/src/meta/query.rs
@@ -11,24 +11,24 @@ pub fn query(
target: Target,
/// The location.
#[external]
- location: StableId,
+ location: Location,
/// The location before which to query.
#[named]
#[external]
- before: StableId,
+ before: Location,
/// The location after which to query.
#[named]
#[external]
- after: StableId,
+ after: Location,
) -> Value {
let selector = target.0;
let introspector = vm.vt.introspector;
- let elements = if let Some(id) = args.named("before")? {
- introspector.query_before(selector, id)
- } else if let Some(id) = args.named("after")? {
- introspector.query_after(selector, id)
+ let elements = if let Some(location) = args.named("before")? {
+ introspector.query_before(selector, location)
+ } else if let Some(location) = args.named("after")? {
+ introspector.query_after(selector, location)
} else {
- let _: StableId = args.expect("id")?;
+ let _: Location = args.expect("location")?;
introspector.query(selector)
};
elements.into()
@@ -40,15 +40,11 @@ struct Target(Selector);
cast_from_value! {
Target,
label: Label => Self(Selector::Label(label)),
- func: Func => {
- let Some(id) = func.id() else {
- return Err("this function is not selectable".into());
- };
-
- if !Content::new(id).can::<dyn Locatable>() {
- Err(eco_format!("cannot query for {}s", id.name))?;
+ element: ElemFunc => {
+ if !Content::new(element).can::<dyn Locatable>() {
+ Err(eco_format!("cannot query for {}s", element.name()))?;
}
- Self(Selector::Node(id, None))
+ Self(Selector::Elem(element, None))
}
}
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index 000080d8..6e794d2d 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -1,6 +1,6 @@
-use super::{BibliographyNode, CiteNode, Counter, LocalName, Numbering};
+use super::{BibliographyElem, CiteElem, Counter, LocalName, Numbering};
use crate::prelude::*;
-use crate::text::TextNode;
+use crate::text::TextElem;
/// A reference to a label.
///
@@ -35,8 +35,8 @@ use crate::text::TextNode;
///
/// Display: Reference
/// Category: meta
-#[node(Locatable, Show)]
-pub struct RefNode {
+#[element(Locatable, Show)]
+pub struct RefElem {
/// The target label that should be referenced.
#[required]
pub target: Label,
@@ -63,7 +63,7 @@ pub struct RefNode {
pub supplement: Smart<Option<Supplement>>,
}
-impl Show for RefNode {
+impl Show for RefElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
@@ -72,7 +72,7 @@ impl Show for RefNode {
let target = self.target();
let matches = vt.introspector.query(Selector::Label(self.target()));
- if BibliographyNode::has(vt, &target.0) {
+ if BibliographyElem::has(vt, &target.0) {
if !matches.is_empty() {
bail!(self.span(), "label occurs in the document and its bibliography");
}
@@ -80,7 +80,7 @@ impl Show for RefNode {
return self.to_citation(styles).show(vt, styles);
}
- let [node] = matches.as_slice() else {
+ let [elem] = matches.as_slice() else {
bail!(self.span(), if matches.is_empty() {
"label does not exist in the document"
} else {
@@ -88,50 +88,50 @@ impl Show for RefNode {
});
};
- if !node.can::<dyn Locatable>() {
- bail!(self.span(), "cannot reference {}", node.id().name);
+ if !elem.can::<dyn Locatable>() {
+ bail!(self.span(), "cannot reference {}", elem.func().name());
}
let supplement = self.supplement(styles);
let mut supplement = match supplement {
- Smart::Auto => node
+ Smart::Auto => elem
.with::<dyn LocalName>()
- .map(|node| node.local_name(TextNode::lang_in(styles)))
- .map(TextNode::packed)
+ .map(|elem| elem.local_name(TextElem::lang_in(styles)))
+ .map(TextElem::packed)
.unwrap_or_default(),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(Supplement::Content(content))) => content.clone(),
Smart::Custom(Some(Supplement::Func(func))) => {
- func.call_vt(vt, [node.clone().into()])?.display()
+ func.call_vt(vt, [elem.clone().into()])?.display()
}
};
if !supplement.is_empty() {
- supplement += TextNode::packed('\u{a0}');
+ supplement += TextElem::packed('\u{a0}');
}
- let Some(numbering) = node.cast_field::<Numbering>("numbering") else {
+ let Some(numbering) = elem.cast_field::<Numbering>("numbering") else {
bail!(self.span(), "only numbered elements can be referenced");
};
- let numbers = Counter::of(node.id())
- .at(vt, node.stable_id().unwrap())?
+ let numbers = Counter::of(elem.func())
+ .at(vt, elem.location().unwrap())?
.display(vt, &numbering.trimmed())?;
- Ok((supplement + numbers).linked(Link::Node(node.stable_id().unwrap())))
+ Ok((supplement + numbers).linked(Destination::Location(elem.location().unwrap())))
}
}
-impl RefNode {
+impl RefElem {
/// Turn the rference into a citation.
- pub fn to_citation(&self, styles: StyleChain) -> CiteNode {
- let mut node = CiteNode::new(vec![self.target().0]);
- node.push_supplement(match self.supplement(styles) {
+ pub fn to_citation(&self, styles: StyleChain) -> CiteElem {
+ let mut elem = CiteElem::new(vec![self.target().0]);
+ elem.push_supplement(match self.supplement(styles) {
Smart::Custom(Some(Supplement::Content(content))) => Some(content),
_ => None,
});
- node.0.set_stable_id(self.0.stable_id().unwrap());
- node
+ elem.0.set_location(self.0.location().unwrap());
+ elem
}
}
diff --git a/library/src/meta/state.rs b/library/src/meta/state.rs
index b19a2671..090f1ccf 100644
--- a/library/src/meta/state.rs
+++ b/library/src/meta/state.rs
@@ -52,25 +52,25 @@ impl State {
/// Display the current value of the state.
pub fn display(self, func: Option<Func>) -> Content {
- DisplayNode::new(self, func).pack()
+ DisplayElem::new(self, func).pack()
}
/// Get the value of the state at the given location.
- pub fn at(self, vt: &mut Vt, id: StableId) -> SourceResult<Value> {
+ pub fn at(self, vt: &mut Vt, location: Location) -> SourceResult<Value> {
let sequence = self.sequence(vt)?;
- let offset = vt.introspector.query_before(self.selector(), id).len();
+ let offset = vt.introspector.query_before(self.selector(), location).len();
Ok(sequence[offset].clone())
}
/// Get the value of the state at the final location.
- pub fn final_(self, vt: &mut Vt, _: StableId) -> SourceResult<Value> {
+ pub fn final_(self, vt: &mut Vt, _: Location) -> SourceResult<Value> {
let sequence = self.sequence(vt)?;
Ok(sequence.last().unwrap().clone())
}
/// Produce content that performs a state update.
pub fn update(self, update: StateUpdate) -> Content {
- UpdateNode::new(self, update).pack()
+ UpdateElem::new(self, update).pack()
}
/// Produce the whole sequence of states.
@@ -99,9 +99,9 @@ impl State {
let mut state = self.init.clone();
let mut stops = eco_vec![state.clone()];
- for node in introspector.query(self.selector()) {
- let node = node.to::<UpdateNode>().unwrap();
- match node.update() {
+ for elem in introspector.query(self.selector()) {
+ let elem = elem.to::<UpdateElem>().unwrap();
+ match elem.update() {
StateUpdate::Set(value) => state = value,
StateUpdate::Func(func) => state = func.call_vt(&mut vt, [state])?,
}
@@ -113,10 +113,7 @@ impl State {
/// The selector for this state's updates.
fn selector(&self) -> Selector {
- Selector::Node(
- NodeId::of::<UpdateNode>(),
- Some(dict! { "state" => self.clone() }),
- )
+ Selector::Elem(UpdateElem::func(), Some(dict! { "state" => self.clone() }))
}
}
@@ -159,8 +156,8 @@ cast_from_value! {
///
/// Display: State
/// Category: special
-#[node(Locatable, Show)]
-struct DisplayNode {
+#[element(Locatable, Show)]
+struct DisplayElem {
/// The state.
#[required]
state: State,
@@ -170,10 +167,10 @@ struct DisplayNode {
func: Option<Func>,
}
-impl Show for DisplayNode {
+impl Show for DisplayElem {
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
- let id = self.0.stable_id().unwrap();
- let value = self.state().at(vt, id)?;
+ let location = self.0.location().unwrap();
+ let value = self.state().at(vt, location)?;
Ok(match self.func() {
Some(func) => func.call_vt(vt, [value])?.display(),
None => value.display(),
@@ -185,8 +182,8 @@ impl Show for DisplayNode {
///
/// Display: State
/// Category: special
-#[node(Locatable, Show)]
-struct UpdateNode {
+#[element(Locatable, Show)]
+struct UpdateElem {
/// The state.
#[required]
state: State,
@@ -196,7 +193,7 @@ struct UpdateNode {
update: StateUpdate,
}
-impl Show for UpdateNode {
+impl Show for UpdateElem {
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(Content::empty())
}
diff --git a/library/src/prelude.rs b/library/src/prelude.rs
index 55e5f17b..4c83cf31 100644
--- a/library/src/prelude.rs
+++ b/library/src/prelude.rs
@@ -22,9 +22,9 @@ pub use typst::eval::{
pub use typst::geom::*;
#[doc(no_inline)]
pub use typst::model::{
- node, Behave, Behaviour, Construct, Content, Finalize, Fold, Introspector, Label,
- Locatable, MetaNode, Node, NodeId, Resolve, Selector, Set, Show, StabilityProvider,
- StableId, StyleChain, StyleMap, StyleVec, Synthesize, Unlabellable, Vt,
+ element, Behave, Behaviour, Construct, Content, ElemFunc, Element, Finalize, Fold,
+ Introspector, Label, Locatable, Location, MetaElem, Resolve, Selector, Set, Show,
+ StabilityProvider, StyleChain, StyleVec, Styles, Synthesize, Unlabellable, Vt,
};
#[doc(no_inline)]
pub use typst::syntax::{Span, Spanned};
@@ -36,4 +36,4 @@ pub use typst::World;
#[doc(no_inline)]
pub use crate::layout::{Fragment, Layout, Regions};
#[doc(no_inline)]
-pub use crate::shared::{ContentExt, StyleMapExt};
+pub use crate::shared::{ContentExt, StylesExt};
diff --git a/library/src/shared/behave.rs b/library/src/shared/behave.rs
index eff41c0b..6a1aa127 100644
--- a/library/src/shared/behave.rs
+++ b/library/src/shared/behave.rs
@@ -1,14 +1,15 @@
-//! Node interaction.
+//! Element interaction.
use typst::model::{Behave, Behaviour, Content, StyleChain, StyleVec, StyleVecBuilder};
-/// A wrapper around a [`StyleVecBuilder`] that allows items to interact.
+/// A wrapper around a [`StyleVecBuilder`] that allows elements to interact.
#[derive(Debug)]
pub struct BehavedBuilder<'a> {
/// The internal builder.
builder: StyleVecBuilder<'a, Content>,
- /// Staged weak and ignorant items that we can't yet commit to the builder.
- /// The option is `Some(_)` for weak items and `None` for ignorant items.
+ /// Staged weak and ignorant elements that we can't yet commit to the
+ /// builder. The option is `Some(_)` for weak elements and `None` for
+ /// ignorant elements.
staged: Vec<(Content, Behaviour, StyleChain<'a>)>,
/// What the last non-ignorant item was.
last: Behaviour,
@@ -29,7 +30,7 @@ impl<'a> BehavedBuilder<'a> {
self.builder.is_empty() && self.staged.is_empty()
}
- /// Whether the builder is empty except for some weak items that will
+ /// Whether the builder is empty except for some weak elements that will
/// probably collapse.
pub fn is_basically_empty(&self) -> bool {
self.builder.is_empty()
@@ -40,15 +41,15 @@ impl<'a> BehavedBuilder<'a> {
}
/// Push an item into the sequence.
- pub fn push(&mut self, item: Content, styles: StyleChain<'a>) {
- let interaction = item
+ pub fn push(&mut self, elem: Content, styles: StyleChain<'a>) {
+ let interaction = elem
.with::<dyn Behave>()
.map_or(Behaviour::Supportive, Behave::behaviour);
match interaction {
Behaviour::Weak(level) => {
if matches!(self.last, Behaviour::Weak(_)) {
- let item = item.with::<dyn Behave>().unwrap();
+ let item = elem.with::<dyn Behave>().unwrap();
let i = self.staged.iter().position(|prev| {
let Behaviour::Weak(prev_level) = prev.1 else { return false };
level < prev_level
@@ -59,29 +60,29 @@ impl<'a> BehavedBuilder<'a> {
}
if self.last != Behaviour::Destructive {
- self.staged.push((item, interaction, styles));
+ self.staged.push((elem, interaction, styles));
self.last = interaction;
}
}
Behaviour::Supportive => {
self.flush(true);
- self.builder.push(item, styles);
+ self.builder.push(elem, styles);
self.last = interaction;
}
Behaviour::Destructive => {
self.flush(false);
- self.builder.push(item, styles);
+ self.builder.push(elem, styles);
self.last = interaction;
}
Behaviour::Ignorant => {
- self.staged.push((item, interaction, styles));
+ self.staged.push((elem, interaction, styles));
}
}
}
- /// Iterate over the contained items.
- pub fn items(&self) -> impl DoubleEndedIterator<Item = &Content> {
- self.builder.items().chain(self.staged.iter().map(|(item, ..)| item))
+ /// Iterate over the contained elements.
+ pub fn elems(&self) -> impl DoubleEndedIterator<Item = &Content> {
+ self.builder.elems().chain(self.staged.iter().map(|(item, ..)| item))
}
/// Return the finish style vec and the common prefix chain.
@@ -90,7 +91,7 @@ impl<'a> BehavedBuilder<'a> {
self.builder.finish()
}
- /// Push the staged items, filtering out weak items if `supportive` is
+ /// Push the staged elements, filtering out weak elements if `supportive` is
/// false.
fn flush(&mut self, supportive: bool) {
for (item, interaction, styles) in self.staged.drain(..) {
diff --git a/library/src/shared/ext.rs b/library/src/shared/ext.rs
index 14674c9d..72a82749 100644
--- a/library/src/shared/ext.rs
+++ b/library/src/shared/ext.rs
@@ -1,8 +1,8 @@
//! Extension traits.
-use crate::layout::{AlignNode, MoveNode, PadNode};
+use crate::layout::{AlignElem, MoveElem, PadElem};
use crate::prelude::*;
-use crate::text::{EmphNode, FontFamily, FontList, StrongNode, TextNode, UnderlineNode};
+use crate::text::{EmphElem, FontFamily, FontList, StrongElem, TextElem, UnderlineElem};
/// Additional methods on content.
pub trait ContentExt {
@@ -16,7 +16,7 @@ pub trait ContentExt {
fn underlined(self) -> Self;
/// Link the content somewhere.
- fn linked(self, link: Link) -> Self;
+ fn linked(self, dest: Destination) -> Self;
/// Set alignments for this content.
fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self;
@@ -30,27 +30,27 @@ pub trait ContentExt {
impl ContentExt for Content {
fn strong(self) -> Self {
- StrongNode::new(self).pack()
+ StrongElem::new(self).pack()
}
fn emph(self) -> Self {
- EmphNode::new(self).pack()
+ EmphElem::new(self).pack()
}
fn underlined(self) -> Self {
- UnderlineNode::new(self).pack()
+ UnderlineElem::new(self).pack()
}
- fn linked(self, link: Link) -> Self {
- self.styled(MetaNode::set_data(vec![Meta::Link(link)]))
+ fn linked(self, dest: Destination) -> Self {
+ self.styled(MetaElem::set_data(vec![Meta::Link(dest)]))
}
fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self {
- self.styled(AlignNode::set_alignment(aligns))
+ self.styled(AlignElem::set_alignment(aligns))
}
fn padded(self, padding: Sides<Rel<Length>>) -> Self {
- PadNode::new(self)
+ PadElem::new(self)
.with_left(padding.left)
.with_top(padding.top)
.with_right(padding.right)
@@ -59,22 +59,22 @@ impl ContentExt for Content {
}
fn moved(self, delta: Axes<Rel<Length>>) -> Self {
- MoveNode::new(self).with_dx(delta.x).with_dy(delta.y).pack()
+ MoveElem::new(self).with_dx(delta.x).with_dy(delta.y).pack()
}
}
-/// Additional methods for style maps.
-pub trait StyleMapExt {
+/// Additional methods for style lists.
+pub trait StylesExt {
/// Set a font family composed of a preferred family and existing families
/// from a style chain.
fn set_family(&mut self, preferred: FontFamily, existing: StyleChain);
}
-impl StyleMapExt for StyleMap {
+impl StylesExt for Styles {
fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) {
- self.set(TextNode::set_font(FontList(
+ self.set(TextElem::set_font(FontList(
std::iter::once(preferred)
- .chain(TextNode::font_in(existing))
+ .chain(TextElem::font_in(existing))
.collect(),
)));
}
diff --git a/library/src/symbols/emoji.rs b/library/src/symbols/emoji.rs
index 07c7004b..5db3a799 100644
--- a/library/src/symbols/emoji.rs
+++ b/library/src/symbols/emoji.rs
@@ -993,7 +993,7 @@ const EMOJI: &[(&'static str, Symbol)] = symbols! {
piano: '🎹',
pick: '⛏',
pie: '🥧',
- pig: ['🐖', face: '🐷', node: '🐽'],
+ pig: ['🐖', face: '🐷', nose: '🐽'],
pill: '💊',
pin: ['📌', round: '📍'],
pinata: '🪅',
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index a29564f3..90a6ca85 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -1,7 +1,7 @@
use kurbo::{BezPath, Line, ParamCurve};
use ttf_parser::{GlyphId, OutlineBuilder};
-use super::TextNode;
+use super::TextElem;
use crate::prelude::*;
/// Underline text.
@@ -13,8 +13,8 @@ use crate::prelude::*;
///
/// Display: Underline
/// Category: text
-#[node(Show)]
-pub struct UnderlineNode {
+#[element(Show)]
+pub struct UnderlineElem {
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -65,9 +65,9 @@ pub struct UnderlineNode {
pub body: Content,
}
-impl Show for UnderlineNode {
+impl Show for UnderlineElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_deco(Decoration {
+ Ok(self.body().styled(TextElem::set_deco(Decoration {
line: DecoLine::Underline,
stroke: self.stroke(styles).unwrap_or_default(),
offset: self.offset(styles),
@@ -86,8 +86,8 @@ impl Show for UnderlineNode {
///
/// Display: Overline
/// Category: text
-#[node(Show)]
-pub struct OverlineNode {
+#[element(Show)]
+pub struct OverlineElem {
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -144,9 +144,9 @@ pub struct OverlineNode {
pub body: Content,
}
-impl Show for OverlineNode {
+impl Show for OverlineElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_deco(Decoration {
+ Ok(self.body().styled(TextElem::set_deco(Decoration {
line: DecoLine::Overline,
stroke: self.stroke(styles).unwrap_or_default(),
offset: self.offset(styles),
@@ -165,8 +165,8 @@ impl Show for OverlineNode {
///
/// Display: Strikethrough
/// Category: text
-#[node(Show)]
-pub struct StrikeNode {
+#[element(Show)]
+pub struct StrikeElem {
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -208,9 +208,9 @@ pub struct StrikeNode {
pub body: Content,
}
-impl Show for StrikeNode {
+impl Show for StrikeElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_deco(Decoration {
+ Ok(self.body().styled(TextElem::set_deco(Decoration {
line: DecoLine::Strikethrough,
stroke: self.stroke(styles).unwrap_or_default(),
offset: self.offset(styles),
@@ -255,7 +255,7 @@ pub enum DecoLine {
pub(super) fn decorate(
frame: &mut Frame,
deco: &Decoration,
- text: &Text,
+ text: &TextItem,
shift: Abs,
pos: Point,
width: Abs,
@@ -285,7 +285,7 @@ pub(super) fn decorate(
if target.x >= min_width || !deco.evade {
let shape = Geometry::Line(target).stroked(stroke);
- frame.push(origin, Element::Shape(shape, Span::detached()));
+ frame.push(origin, FrameItem::Shape(shape, Span::detached()));
}
};
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index 5a5c8514..e1d9c0f2 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -1,20 +1,20 @@
-use super::TextNode;
+use super::TextElem;
use crate::prelude::*;
/// A text space.
///
/// Display: Space
/// Category: text
-#[node(Unlabellable, Behave)]
-pub struct SpaceNode {}
+#[element(Unlabellable, Behave)]
+pub struct SpaceElem {}
-impl Behave for SpaceNode {
+impl Behave for SpaceElem {
fn behaviour(&self) -> Behaviour {
Behaviour::Weak(2)
}
}
-impl Unlabellable for SpaceNode {}
+impl Unlabellable for SpaceElem {}
/// Inserts a line break.
///
@@ -36,8 +36,8 @@ impl Unlabellable for SpaceNode {}
///
/// Display: Line Break
/// Category: text
-#[node(Behave)]
-pub struct LinebreakNode {
+#[element(Behave)]
+pub struct LinebreakElem {
/// Whether to justify the line before the break.
///
/// This is useful if you found a better line break opportunity in your
@@ -55,7 +55,7 @@ pub struct LinebreakNode {
pub justify: bool,
}
-impl Behave for LinebreakNode {
+impl Behave for LinebreakElem {
fn behaviour(&self) -> Behaviour {
Behaviour::Destructive
}
@@ -82,8 +82,8 @@ impl Behave for LinebreakNode {
///
/// Display: Strong Emphasis
/// Category: text
-#[node(Show)]
-pub struct StrongNode {
+#[element(Show)]
+pub struct StrongElem {
/// The delta to apply on the font weight.
///
/// ```example
@@ -98,9 +98,9 @@ pub struct StrongNode {
pub body: Content,
}
-impl Show for StrongNode {
+impl Show for StrongElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_delta(Delta(self.delta(styles)))))
+ Ok(self.body().styled(TextElem::set_delta(Delta(self.delta(styles)))))
}
}
@@ -151,16 +151,16 @@ impl Fold for Delta {
///
/// Display: Emphasis
/// Category: text
-#[node(Show)]
-pub struct EmphNode {
+#[element(Show)]
+pub struct EmphElem {
/// The content to emphasize.
#[required]
pub body: Content,
}
-impl Show for EmphNode {
+impl Show for EmphElem {
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_emph(Toggle)))
+ Ok(self.body().styled(TextElem::set_emph(Toggle)))
}
}
@@ -229,7 +229,7 @@ pub fn upper(
fn case(text: ToCase, case: Case) -> Value {
match text {
ToCase::Str(v) => Value::Str(case.apply(&v).into()),
- ToCase::Content(v) => Value::Content(v.styled(TextNode::set_case(Some(case)))),
+ ToCase::Content(v) => Value::Content(v.styled(TextElem::set_case(Some(case)))),
}
}
@@ -295,7 +295,7 @@ pub fn smallcaps(
/// The text to display to small capitals.
body: Content,
) -> Value {
- Value::Content(body.styled(TextNode::set_smallcaps(true)))
+ Value::Content(body.styled(TextElem::set_smallcaps(true)))
}
/// Create blind text.
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index 845ffe29..16268aad 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -19,7 +19,7 @@ use std::borrow::Cow;
use rustybuzz::Tag;
use typst::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
-use crate::layout::ParNode;
+use crate::layout::ParElem;
use crate::prelude::*;
/// Customize the look and layout of text in a variety of ways.
@@ -40,8 +40,8 @@ use crate::prelude::*;
///
/// Display: Text
/// Category: text
-#[node(Construct)]
-pub struct TextNode {
+#[element(Construct)]
+pub struct TextElem {
/// A prioritized sequence of font families.
///
/// When processing text, Typst tries all specified font families in order
@@ -291,7 +291,7 @@ pub struct TextNode {
/// هذا عربي.
/// ```
#[resolve]
- pub dir: HorizontalDir,
+ pub dir: TextDir,
/// Whether to hyphenate text to improve line breaking. When `{auto}`, text
/// will be hyphenated if and only if justification is enabled.
@@ -479,16 +479,16 @@ pub struct TextNode {
pub smallcaps: bool,
}
-impl TextNode {
- /// Create a new packed text node.
+impl TextElem {
+ /// Create a new packed text element.
pub fn packed(text: impl Into<EcoString>) -> Content {
Self::new(text.into()).pack()
}
}
-impl Construct for TextNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- // The text constructor is special: It doesn't create a text node.
+impl Construct for TextElem {
+ fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
+ // The text constructor is special: It doesn't create a text element.
// Instead, it leaves the passed argument structurally unchanged, but
// styles all text in it.
let styles = Self::set(args)?;
@@ -606,28 +606,28 @@ cast_to_value! {
/// The direction of text and inline objects in their line.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct HorizontalDir(pub Smart<Dir>);
+pub struct TextDir(pub Smart<Dir>);
cast_from_value! {
- HorizontalDir,
+ TextDir,
v: Smart<Dir> => {
if v.map_or(false, |dir| dir.axis() == Axis::Y) {
- Err("must be horizontal")?;
+ Err("text direction must be horizontal")?;
}
Self(v)
},
}
cast_to_value! {
- v: HorizontalDir => v.0.into()
+ v: TextDir => v.0.into()
}
-impl Resolve for HorizontalDir {
+impl Resolve for TextDir {
type Output = Dir;
fn resolve(self, styles: StyleChain) -> Self::Output {
match self.0 {
- Smart::Auto => TextNode::lang_in(styles).dir(),
+ Smart::Auto => TextElem::lang_in(styles).dir(),
Smart::Custom(dir) => dir,
}
}
@@ -651,7 +651,7 @@ impl Resolve for Hyphenate {
fn resolve(self, styles: StyleChain) -> Self::Output {
match self.0 {
- Smart::Auto => ParNode::justify_in(styles),
+ Smart::Auto => ParElem::justify_in(styles),
Smart::Custom(v) => v,
}
}
@@ -677,7 +677,7 @@ cast_from_value! {
StylisticSet,
v: i64 => match v {
1 ..= 20 => Self::new(v as u8),
- _ => Err("must be between 1 and 20")?,
+ _ => Err("stylistic set must be between 1 and 20")?,
},
}
diff --git a/library/src/text/quotes.rs b/library/src/text/quotes.rs
index 863cc3bd..be923304 100644
--- a/library/src/text/quotes.rs
+++ b/library/src/text/quotes.rs
@@ -24,8 +24,8 @@ use crate::prelude::*;
///
/// Display: Smart Quote
/// Category: text
-#[node]
-pub struct SmartQuoteNode {
+#[element]
+pub struct SmartQuoteElem {
/// Whether this should be a double quote.
#[default(true)]
pub double: bool,
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index b6cc0d3d..d24254ed 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -3,9 +3,9 @@ use syntect::highlighting as synt;
use typst::syntax::{self, LinkedNode};
use super::{
- FontFamily, FontList, Hyphenate, LinebreakNode, SmartQuoteNode, TextNode, TextSize,
+ FontFamily, FontList, Hyphenate, LinebreakElem, SmartQuoteElem, TextElem, TextSize,
};
-use crate::layout::BlockNode;
+use crate::layout::BlockElem;
use crate::prelude::*;
/// Raw text with optional syntax highlighting.
@@ -35,8 +35,8 @@ use crate::prelude::*;
///
/// Display: Raw Text / Code
/// Category: text
-#[node(Synthesize, Show, Finalize)]
-pub struct RawNode {
+#[element(Synthesize, Show, Finalize)]
+pub struct RawElem {
/// The raw text.
///
/// You can also use raw blocks creatively to create custom syntaxes for
@@ -103,7 +103,7 @@ pub struct RawNode {
pub lang: Option<EcoString>,
}
-impl RawNode {
+impl RawElem {
/// The supported language names and tags.
pub fn languages() -> Vec<(&'static str, Vec<&'static str>)> {
SYNTAXES
@@ -120,13 +120,13 @@ impl RawNode {
}
}
-impl Synthesize for RawNode {
+impl Synthesize for RawElem {
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
self.push_lang(self.lang(styles));
}
}
-impl Show for RawNode {
+impl Show for RawElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let text = self.text();
let lang = self.lang(styles).as_ref().map(|s| s.to_lowercase());
@@ -162,7 +162,7 @@ impl Show for RawNode {
let mut highlighter = syntect::easy::HighlightLines::new(syntax, &THEME);
for (i, line) in text.lines().enumerate() {
if i != 0 {
- seq.push(LinebreakNode::new().pack());
+ seq.push(LinebreakElem::new().pack());
}
for (style, piece) in
@@ -174,26 +174,27 @@ impl Show for RawNode {
Content::sequence(seq)
} else {
- TextNode::packed(text)
+ TextElem::packed(text)
};
if self.block(styles) {
- realized = BlockNode::new().with_body(Some(realized)).pack();
+ realized = BlockElem::new().with_body(Some(realized)).pack();
}
Ok(realized)
}
}
-impl Finalize for RawNode {
+impl Finalize for RawElem {
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
- let mut map = StyleMap::new();
- map.set(TextNode::set_overhang(false));
- map.set(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))));
- map.set(TextNode::set_size(TextSize(Em::new(0.8).into())));
- map.set(TextNode::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")])));
- map.set(SmartQuoteNode::set_enabled(false));
- realized.styled_with_map(map)
+ let mut styles = Styles::new();
+ styles.set(TextElem::set_overhang(false));
+ styles.set(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))));
+ styles.set(TextElem::set_size(TextSize(Em::new(0.8).into())));
+ styles
+ .set(TextElem::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")])));
+ styles.set(SmartQuoteElem::set_enabled(false));
+ realized.styled_with_map(styles)
}
}
@@ -224,11 +225,11 @@ fn highlight_themed<F>(
/// Style a piece of text with a syntect style.
fn styled(piece: &str, foreground: Paint, style: synt::Style) -> Content {
- let mut body = TextNode::packed(piece);
+ let mut body = TextElem::packed(piece);
let paint = to_typst(style.foreground).into();
if paint != foreground {
- body = body.styled(TextNode::set_fill(paint));
+ body = body.styled(TextElem::set_fill(paint));
}
if style.font_style.contains(synt::FontStyle::BOLD) {
diff --git a/library/src/text/shaping.rs b/library/src/text/shaping.rs
index 244e7afe..15fbcd3f 100644
--- a/library/src/text/shaping.rs
+++ b/library/src/text/shaping.rs
@@ -95,10 +95,10 @@ impl<'a> ShapedText<'a> {
let mut frame = Frame::new(size);
frame.set_baseline(top);
- let shift = TextNode::baseline_in(self.styles);
- let lang = TextNode::lang_in(self.styles);
- let decos = TextNode::deco_in(self.styles);
- let fill = TextNode::fill_in(self.styles);
+ let shift = TextElem::baseline_in(self.styles);
+ let lang = TextElem::lang_in(self.styles);
+ let decos = TextElem::deco_in(self.styles);
+ let fill = TextElem::fill_in(self.styles);
for ((font, y_offset), group) in
self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset))
@@ -122,16 +122,16 @@ impl<'a> ShapedText<'a> {
})
.collect();
- let text = Text { font, size: self.size, lang, fill, glyphs };
- let text_layer = frame.layer();
- let width = text.width();
+ let item = TextItem { font, size: self.size, lang, fill, glyphs };
+ let layer = frame.layer();
+ let width = item.width();
// Apply line decorations.
for deco in &decos {
- decorate(&mut frame, deco, &text, shift, pos, width);
+ decorate(&mut frame, deco, &item, shift, pos, width);
}
- frame.insert(text_layer, pos, Element::Text(text));
+ frame.insert(layer, pos, FrameItem::Text(item));
offset += width;
}
@@ -146,8 +146,8 @@ impl<'a> ShapedText<'a> {
let mut top = Abs::zero();
let mut bottom = Abs::zero();
- let top_edge = TextNode::top_edge_in(self.styles);
- let bottom_edge = TextNode::bottom_edge_in(self.styles);
+ let top_edge = TextElem::top_edge_in(self.styles);
+ let bottom_edge = TextElem::bottom_edge_in(self.styles);
// Expand top and bottom by reading the font's vertical metrics.
let mut expand = |font: &Font| {
@@ -343,7 +343,7 @@ pub fn shape<'a>(
styles: StyleChain<'a>,
dir: Dir,
) -> ShapedText<'a> {
- let size = TextNode::size_in(styles);
+ let size = TextElem::size_in(styles);
let mut ctx = ShapingContext {
vt,
base,
@@ -354,7 +354,7 @@ pub fn shape<'a>(
styles,
variant: variant(styles),
tags: tags(styles),
- fallback: TextNode::fallback_in(styles),
+ fallback: TextElem::fallback_in(styles),
dir,
};
@@ -531,9 +531,9 @@ fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font: Font) {
/// Apply tracking and spacing to the shaped glyphs.
fn track_and_space(ctx: &mut ShapingContext) {
- let tracking = Em::from_length(TextNode::tracking_in(ctx.styles), ctx.size);
+ let tracking = Em::from_length(TextElem::tracking_in(ctx.styles), ctx.size);
let spacing =
- TextNode::spacing_in(ctx.styles).map(|abs| Em::from_length(abs, ctx.size));
+ TextElem::spacing_in(ctx.styles).map(|abs| Em::from_length(abs, ctx.size));
let mut glyphs = ctx.glyphs.iter_mut().peekable();
while let Some(glyph) = glyphs.next() {
@@ -562,17 +562,17 @@ fn nbsp_delta(font: &Font) -> Option<Em> {
/// Resolve the font variant.
pub fn variant(styles: StyleChain) -> FontVariant {
let mut variant = FontVariant::new(
- TextNode::style_in(styles),
- TextNode::weight_in(styles),
- TextNode::stretch_in(styles),
+ TextElem::style_in(styles),
+ TextElem::weight_in(styles),
+ TextElem::stretch_in(styles),
);
- let delta = TextNode::delta_in(styles);
+ let delta = TextElem::delta_in(styles);
variant.weight = variant
.weight
.thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16);
- if TextNode::emph_in(styles) {
+ if TextElem::emph_in(styles) {
variant.style = match variant.style {
FontStyle::Normal => FontStyle::Italic,
FontStyle::Italic => FontStyle::Normal,
@@ -593,8 +593,8 @@ pub fn families(styles: StyleChain) -> impl Iterator<Item = FontFamily> + Clone
"segoe ui emoji",
];
- let tail = if TextNode::fallback_in(styles) { FALLBACKS } else { &[] };
- TextNode::font_in(styles)
+ let tail = if TextElem::fallback_in(styles) { FALLBACKS } else { &[] };
+ TextElem::font_in(styles)
.into_iter()
.chain(tail.iter().copied().map(FontFamily::new))
}
@@ -607,59 +607,59 @@ fn tags(styles: StyleChain) -> Vec<Feature> {
};
// Features that are on by default in Harfbuzz are only added if disabled.
- if !TextNode::kerning_in(styles) {
+ if !TextElem::kerning_in(styles) {
feat(b"kern", 0);
}
// Features that are off by default in Harfbuzz are only added if enabled.
- if TextNode::smallcaps_in(styles) {
+ if TextElem::smallcaps_in(styles) {
feat(b"smcp", 1);
}
- if TextNode::alternates_in(styles) {
+ if TextElem::alternates_in(styles) {
feat(b"salt", 1);
}
let storage;
- if let Some(set) = TextNode::stylistic_set_in(styles) {
+ if let Some(set) = TextElem::stylistic_set_in(styles) {
storage = [b's', b's', b'0' + set.get() / 10, b'0' + set.get() % 10];
feat(&storage, 1);
}
- if !TextNode::ligatures_in(styles) {
+ if !TextElem::ligatures_in(styles) {
feat(b"liga", 0);
feat(b"clig", 0);
}
- if TextNode::discretionary_ligatures_in(styles) {
+ if TextElem::discretionary_ligatures_in(styles) {
feat(b"dlig", 1);
}
- if TextNode::historical_ligatures_in(styles) {
+ if TextElem::historical_ligatures_in(styles) {
feat(b"hilg", 1);
}
- match TextNode::number_type_in(styles) {
+ match TextElem::number_type_in(styles) {
Smart::Auto => {}
Smart::Custom(NumberType::Lining) => feat(b"lnum", 1),
Smart::Custom(NumberType::OldStyle) => feat(b"onum", 1),
}
- match TextNode::number_width_in(styles) {
+ match TextElem::number_width_in(styles) {
Smart::Auto => {}
Smart::Custom(NumberWidth::Proportional) => feat(b"pnum", 1),
Smart::Custom(NumberWidth::Tabular) => feat(b"tnum", 1),
}
- if TextNode::slashed_zero_in(styles) {
+ if TextElem::slashed_zero_in(styles) {
feat(b"zero", 1);
}
- if TextNode::fractions_in(styles) {
+ if TextElem::fractions_in(styles) {
feat(b"frac", 1);
}
- for (tag, value) in TextNode::features_in(styles).0 {
+ for (tag, value) in TextElem::features_in(styles).0 {
tags.push(Feature::new(tag, value, ..))
}
@@ -669,8 +669,8 @@ fn tags(styles: StyleChain) -> Vec<Feature> {
/// Process the language and and region of a style chain into a
/// rustybuzz-compatible BCP 47 language.
fn language(styles: StyleChain) -> rustybuzz::Language {
- let mut bcp: EcoString = TextNode::lang_in(styles).as_str().into();
- if let Some(region) = TextNode::region_in(styles) {
+ let mut bcp: EcoString = TextElem::lang_in(styles).as_str().into();
+ if let Some(region) = TextElem::region_in(styles) {
bcp.push('-');
bcp.push_str(region.as_str());
}
diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs
index 74bb70c7..1bea3673 100644
--- a/library/src/text/shift.rs
+++ b/library/src/text/shift.rs
@@ -1,6 +1,4 @@
-use typst::model::SequenceNode;
-
-use super::{variant, SpaceNode, TextNode, TextSize};
+use super::{variant, SpaceElem, TextElem, TextSize};
use crate::prelude::*;
/// Set text in subscript.
@@ -14,8 +12,8 @@ use crate::prelude::*;
///
/// Display: Subscript
/// Category: text
-#[node(Show)]
-pub struct SubNode {
+#[element(Show)]
+pub struct SubElem {
/// Whether to prefer the dedicated subscript characters of the font.
///
/// If this is enabled, Typst first tries to transform the text to subscript
@@ -46,21 +44,21 @@ pub struct SubNode {
pub body: Content,
}
-impl Show for SubNode {
+impl Show for SubElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
if self.typographic(styles) {
if let Some(text) = search_text(&body, true) {
if is_shapable(vt, &text, styles) {
- transformed = Some(TextNode::packed(text));
+ transformed = Some(TextElem::packed(text));
}
}
};
Ok(transformed.unwrap_or_else(|| {
- body.styled(TextNode::set_baseline(self.baseline(styles)))
- .styled(TextNode::set_size(self.size(styles)))
+ body.styled(TextElem::set_baseline(self.baseline(styles)))
+ .styled(TextElem::set_size(self.size(styles)))
}))
}
}
@@ -76,8 +74,8 @@ impl Show for SubNode {
///
/// Display: Superscript
/// Category: text
-#[node(Show)]
-pub struct SuperNode {
+#[element(Show)]
+pub struct SuperElem {
/// Whether to prefer the dedicated superscript characters of the font.
///
/// If this is enabled, Typst first tries to transform the text to
@@ -108,35 +106,35 @@ pub struct SuperNode {
pub body: Content,
}
-impl Show for SuperNode {
+impl Show for SuperElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
if self.typographic(styles) {
if let Some(text) = search_text(&body, false) {
if is_shapable(vt, &text, styles) {
- transformed = Some(TextNode::packed(text));
+ transformed = Some(TextElem::packed(text));
}
}
};
Ok(transformed.unwrap_or_else(|| {
- body.styled(TextNode::set_baseline(self.baseline(styles)))
- .styled(TextNode::set_size(self.size(styles)))
+ body.styled(TextElem::set_baseline(self.baseline(styles)))
+ .styled(TextElem::set_size(self.size(styles)))
}))
}
}
/// Find and transform the text contained in `content` to the given script kind
-/// if and only if it only consists of `Text`, `Space`, and `Empty` leaf nodes.
+/// if and only if it only consists of `Text`, `Space`, and `Empty` leafs.
fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
- if content.is::<SpaceNode>() {
+ if content.is::<SpaceElem>() {
Some(' '.into())
- } else if let Some(node) = content.to::<TextNode>() {
- convert_script(&node.text(), sub)
- } else if let Some(seq) = content.to::<SequenceNode>() {
+ } else if let Some(elem) = content.to::<TextElem>() {
+ convert_script(&elem.text(), sub)
+ } else if let Some(children) = content.to_sequence() {
let mut full = EcoString::new();
- for item in seq.children() {
+ for item in children {
match search_text(&item, sub) {
Some(text) => full.push_str(&text),
None => return None,
@@ -152,7 +150,7 @@ fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
/// given string.
fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool {
let world = vt.world;
- for family in TextNode::font_in(styles) {
+ for family in TextElem::font_in(styles) {
if let Some(font) = world
.book()
.select(family.as_str(), variant(styles))
diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs
index c5016436..8a81a40e 100644
--- a/library/src/visualize/image.rs
+++ b/library/src/visualize/image.rs
@@ -22,8 +22,8 @@ use crate::prelude::*;
///
/// Display: Image
/// Category: visualize
-#[node(Layout)]
-pub struct ImageNode {
+#[element(Layout)]
+pub struct ImageElem {
/// Path to an image file.
#[required]
#[parse(
@@ -46,7 +46,7 @@ pub struct ImageNode {
pub fit: ImageFit,
}
-impl Layout for ImageNode {
+impl Layout for ImageElem {
fn layout(
&self,
vt: &mut Vt,
@@ -97,7 +97,7 @@ impl Layout for ImageNode {
// the frame to the target size, center aligning the image in the
// process.
let mut frame = Frame::new(fitted);
- frame.push(Point::zero(), Element::Image(image, fitted, self.span()));
+ frame.push(Point::zero(), FrameItem::Image(image, fitted, self.span()));
frame.resize(target, Align::CENTER_HORIZON);
// Create a clipping group if only part of the image should be visible.
diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs
index 6614e3ee..0932a9f1 100644
--- a/library/src/visualize/line.rs
+++ b/library/src/visualize/line.rs
@@ -11,8 +11,8 @@ use crate::prelude::*;
///
/// Display: Line
/// Category: visualize
-#[node(Layout)]
-pub struct LineNode {
+#[element(Layout)]
+pub struct LineElem {
/// The start point of the line.
///
/// Must be an array of exactly two relative lengths.
@@ -49,7 +49,7 @@ pub struct LineNode {
pub stroke: PartialStroke,
}
-impl Layout for LineNode {
+impl Layout for LineElem {
fn layout(
&self,
_: &mut Vt,
@@ -76,7 +76,7 @@ impl Layout for LineNode {
let mut frame = Frame::new(target);
let shape = Geometry::Line(delta.to_point()).stroked(stroke);
- frame.push(start.to_point(), Element::Shape(shape, self.span()));
+ frame.push(start.to_point(), FrameItem::Shape(shape, self.span()));
Ok(Fragment::frame(frame))
}
}
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index 8aef3629..601f0d85 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -18,8 +18,8 @@ use crate::prelude::*;
///
/// Display: Rectangle
/// Category: visualize
-#[node(Layout)]
-pub struct RectNode {
+#[element(Layout)]
+pub struct RectElem {
/// The rectangle's width, relative to its parent container.
pub width: Smart<Rel<Length>>,
@@ -139,7 +139,7 @@ pub struct RectNode {
pub body: Option<Content>,
}
-impl Layout for RectNode {
+impl Layout for RectElem {
fn layout(
&self,
vt: &mut Vt,
@@ -179,8 +179,8 @@ impl Layout for RectNode {
///
/// Display: Square
/// Category: visualize
-#[node(Layout)]
-pub struct SquareNode {
+#[element(Layout)]
+pub struct SquareElem {
/// The square's side length. This is mutually exclusive with `width` and
/// `height`.
#[external]
@@ -249,7 +249,7 @@ pub struct SquareNode {
pub body: Option<Content>,
}
-impl Layout for SquareNode {
+impl Layout for SquareElem {
fn layout(
&self,
vt: &mut Vt,
@@ -290,8 +290,8 @@ impl Layout for SquareNode {
///
/// Display: Ellipse
/// Category: visualize
-#[node(Layout)]
-pub struct EllipseNode {
+#[element(Layout)]
+pub struct EllipseElem {
/// The ellipse's width, relative to its parent container.
pub width: Smart<Rel<Length>>,
@@ -331,7 +331,7 @@ pub struct EllipseNode {
pub body: Option<Content>,
}
-impl Layout for EllipseNode {
+impl Layout for EllipseElem {
fn layout(
&self,
vt: &mut Vt,
@@ -372,8 +372,8 @@ impl Layout for EllipseNode {
///
/// Display: Circle
/// Category: visualize
-#[node(Layout)]
-pub struct CircleNode {
+#[element(Layout)]
+pub struct CircleElem {
/// The circle's radius. This is mutually exclusive with `width` and
/// `height`.
#[external]
@@ -438,7 +438,7 @@ pub struct CircleNode {
pub body: Option<Content>,
}
-impl Layout for CircleNode {
+impl Layout for CircleElem {
fn layout(
&self,
vt: &mut Vt,
@@ -529,7 +529,7 @@ fn layout(
let size = frame.size() + outset.sum_by_axis();
let pos = Point::new(-outset.left, -outset.top);
let shape = ellipse(size, fill, stroke.left);
- frame.prepend(pos, Element::Shape(shape, span));
+ frame.prepend(pos, FrameItem::Shape(shape, span));
} else {
frame.fill_and_stroke(fill, stroke, outset, radius, span);
}
diff --git a/macros/src/node.rs b/macros/src/element.rs
index 68d43d9c..9d6b5c88 100644
--- a/macros/src/node.rs
+++ b/macros/src/element.rs
@@ -1,12 +1,12 @@
use super::*;
-/// Expand the `#[node]` macro.
-pub fn node(stream: TokenStream, body: syn::ItemStruct) -> Result<TokenStream> {
- let node = prepare(stream, &body)?;
- Ok(create(&node))
+/// Expand the `#[element]` macro.
+pub fn element(stream: TokenStream, body: syn::ItemStruct) -> Result<TokenStream> {
+ let element = prepare(stream, &body)?;
+ Ok(create(&element))
}
-struct Node {
+struct Elem {
name: String,
display: String,
category: String,
@@ -65,8 +65,8 @@ impl Parse for FieldParser {
}
}
-/// Preprocess the node's definition.
-fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
+/// Preprocess the element's definition.
+fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Elem> {
let syn::Fields::Named(named) = &body.fields else {
bail!(body, "expected named fields");
};
@@ -143,8 +143,8 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
let display = meta_line(&mut lines, "Display")?.into();
let docs = lines.join("\n").trim().into();
- let node = Node {
- name: body.ident.to_string().trim_end_matches("Node").to_lowercase(),
+ let element = Elem {
+ name: body.ident.to_string().trim_end_matches("Elem").to_lowercase(),
display,
category,
docs,
@@ -155,17 +155,17 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
};
validate_attrs(&body.attrs)?;
- Ok(node)
+ Ok(element)
}
-/// Produce the node's definition.
-fn create(node: &Node) -> TokenStream {
- let Node { vis, ident, docs, .. } = node;
- let all = node.fields.iter().filter(|field| !field.external);
+/// Produce the element's definition.
+fn create(element: &Elem) -> TokenStream {
+ let Elem { vis, ident, docs, .. } = element;
+ let all = element.fields.iter().filter(|field| !field.external);
let settable = all.clone().filter(|field| !field.synthesized && field.settable());
// Inherent methods and functions.
- let new = create_new_func(node);
+ let new = create_new_func(element);
let field_methods = all.clone().map(create_field_method);
let field_in_methods = settable.clone().map(create_field_in_method);
let with_field_methods = all.clone().map(create_with_field_method);
@@ -173,14 +173,14 @@ fn create(node: &Node) -> TokenStream {
let field_style_methods = settable.map(create_set_field_method);
// Trait implementations.
- let node_impl = create_node_impl(node);
- let construct_impl = node
+ let element_impl = create_pack_impl(element);
+ let construct_impl = element
.capable
.iter()
.all(|capability| capability != "Construct")
- .then(|| create_construct_impl(node));
- let set_impl = create_set_impl(node);
- let locatable_impl = node
+ .then(|| create_construct_impl(element));
+ let set_impl = create_set_impl(element);
+ let locatable_impl = element
.capable
.iter()
.any(|capability| capability == "Locatable")
@@ -200,13 +200,13 @@ fn create(node: &Node) -> TokenStream {
#(#push_field_methods)*
#(#field_style_methods)*
- /// The node's span.
+ /// The element's span.
pub fn span(&self) -> ::typst::syntax::Span {
self.0.span()
}
}
- #node_impl
+ #element_impl
#construct_impl
#set_impl
#locatable_impl
@@ -219,9 +219,9 @@ fn create(node: &Node) -> TokenStream {
}
}
-/// Create the `new` function for the node.
-fn create_new_func(node: &Node) -> TokenStream {
- let relevant = node
+/// Create the `new` function for the element.
+fn create_new_func(element: &Elem) -> TokenStream {
+ let relevant = element
.fields
.iter()
.filter(|field| !field.external && !field.synthesized && field.inherent());
@@ -232,9 +232,11 @@ fn create_new_func(node: &Node) -> TokenStream {
quote! { .#with_ident(#ident) }
});
quote! {
- /// Create a new node.
+ /// Create a new element.
pub fn new(#(#params),*) -> Self {
- Self(::typst::model::Content::new(<Self as ::typst::model::Node>::id()))
+ Self(::typst::model::Content::new(
+ <Self as ::typst::model::Element>::func()
+ ))
#(#builder_calls)*
}
}
@@ -252,8 +254,7 @@ fn create_field_method(field: &Field) -> TokenStream {
}
}
} else {
- let access =
- create_style_chain_access(field, quote! { self.0.field(#name).cloned() });
+ let access = create_style_chain_access(field, quote! { self.0.field(#name) });
quote! {
#[doc = #docs]
#vis fn #ident(&self, styles: ::typst::model::StyleChain) -> #output {
@@ -288,7 +289,7 @@ fn create_style_chain_access(field: &Field, inherent: TokenStream) -> TokenStrea
quote! {
styles.#getter::<#ty>(
- ::typst::model::NodeId::of::<Self>(),
+ <Self as ::typst::model::Element>::func(),
#name,
#inherent,
|| #default,
@@ -328,7 +329,7 @@ fn create_set_field_method(field: &Field) -> TokenStream {
#[doc = #doc]
#vis fn #set_ident(#ident: #ty) -> ::typst::model::Style {
::typst::model::Style::Property(::typst::model::Property::new(
- ::typst::model::NodeId::of::<Self>(),
+ <Self as ::typst::model::Element>::func(),
#name.into(),
#ident.into()
))
@@ -336,19 +337,30 @@ fn create_set_field_method(field: &Field) -> TokenStream {
}
}
-/// Create the node's `Node` implementation.
-fn create_node_impl(node: &Node) -> TokenStream {
- let Node { ident, name, display, category, docs, .. } = node;
- let vtable_func = create_vtable_func(node);
- let infos = node
+/// Create the element's `Pack` implementation.
+fn create_pack_impl(element: &Elem) -> TokenStream {
+ let Elem { ident, name, display, category, docs, .. } = element;
+ let vtable_func = create_vtable_func(element);
+ let infos = element
.fields
.iter()
.filter(|field| !field.internal && !field.synthesized)
.map(create_param_info);
quote! {
- impl ::typst::model::Node for #ident {
- fn id() -> ::typst::model::NodeId {
- static META: ::typst::model::NodeMeta = ::typst::model::NodeMeta {
+ impl ::typst::model::Element for #ident {
+ fn pack(self) -> ::typst::model::Content {
+ self.0
+ }
+
+ fn unpack(content: &::typst::model::Content) -> ::std::option::Option<&Self> {
+ // Safety: Elements are #[repr(transparent)].
+ content.is::<Self>().then(|| unsafe {
+ ::std::mem::transmute(content)
+ })
+ }
+
+ fn func() -> ::typst::model::ElemFunc {
+ static NATIVE: ::typst::model::NativeElemFunc = ::typst::model::NativeElemFunc {
name: #name,
vtable: #vtable_func,
construct: <#ident as ::typst::model::Construct>::construct,
@@ -362,20 +374,16 @@ fn create_node_impl(node: &Node) -> TokenStream {
category: #category,
}),
};
- ::typst::model::NodeId(&META)
- }
-
- fn pack(self) -> ::typst::model::Content {
- self.0
+ (&NATIVE).into()
}
}
}
}
-/// Create the node's casting vtable.
-fn create_vtable_func(node: &Node) -> TokenStream {
- let ident = &node.ident;
- let relevant = node.capable.iter().filter(|&ident| ident != "Construct");
+/// Create the element's casting vtable.
+fn create_vtable_func(element: &Elem) -> TokenStream {
+ let ident = &element.ident;
+ let relevant = element.capable.iter().filter(|&ident| ident != "Construct");
let checks = relevant.map(|capability| {
quote! {
if id == ::std::any::TypeId::of::<dyn #capability>() {
@@ -388,7 +396,9 @@ fn create_vtable_func(node: &Node) -> TokenStream {
quote! {
|id| {
- let null = Self(::typst::model::Content::new(<#ident as ::typst::model::Node>::id()));
+ let null = Self(::typst::model::Content::new(
+ <#ident as ::typst::model::Element>::func()
+ ));
#(#checks)*
None
}
@@ -421,10 +431,10 @@ fn create_param_info(field: &Field) -> TokenStream {
}
}
-/// Create the node's `Construct` implementation.
-fn create_construct_impl(node: &Node) -> TokenStream {
- let ident = &node.ident;
- let handlers = node
+/// Create the element's `Construct` implementation.
+fn create_construct_impl(element: &Elem) -> TokenStream {
+ let ident = &element.ident;
+ let handlers = element
.fields
.iter()
.filter(|field| {
@@ -439,13 +449,13 @@ fn create_construct_impl(node: &Node) -> TokenStream {
quote! {
#prefix
if let Some(value) = #value {
- node.#push_ident(value);
+ element.#push_ident(value);
}
}
} else {
quote! {
#prefix
- node.#push_ident(#value);
+ element.#push_ident(#value);
}
}
});
@@ -453,21 +463,23 @@ fn create_construct_impl(node: &Node) -> TokenStream {
quote! {
impl ::typst::model::Construct for #ident {
fn construct(
- vm: &::typst::eval::Vm,
+ vm: &mut ::typst::eval::Vm,
args: &mut ::typst::eval::Args,
) -> ::typst::diag::SourceResult<::typst::model::Content> {
- let mut node = Self(::typst::model::Content::new(<Self as ::typst::model::Node>::id()));
+ let mut element = Self(::typst::model::Content::new(
+ <Self as ::typst::model::Element>::func()
+ ));
#(#handlers)*
- Ok(node.0)
+ Ok(element.0)
}
}
}
}
-/// Create the node's `Set` implementation.
-fn create_set_impl(node: &Node) -> TokenStream {
- let ident = &node.ident;
- let handlers = node
+/// Create the element's `Set` implementation.
+fn create_set_impl(element: &Elem) -> TokenStream {
+ let ident = &element.ident;
+ let handlers = element
.fields
.iter()
.filter(|field| {
@@ -491,8 +503,8 @@ fn create_set_impl(node: &Node) -> TokenStream {
impl ::typst::model::Set for #ident {
fn set(
args: &mut ::typst::eval::Args,
- ) -> ::typst::diag::SourceResult<::typst::model::StyleMap> {
- let mut styles = ::typst::model::StyleMap::new();
+ ) -> ::typst::diag::SourceResult<::typst::model::Styles> {
+ let mut styles = ::typst::model::Styles::new();
#(#handlers)*
Ok(styles)
}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index fafe8eea..945bbcd0 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -5,8 +5,8 @@ extern crate proc_macro;
#[macro_use]
mod util;
mod castable;
+mod element;
mod func;
-mod node;
mod symbols;
use proc_macro::TokenStream as BoundaryStream;
@@ -26,11 +26,11 @@ pub fn func(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
func::func(item).unwrap_or_else(|err| err.to_compile_error()).into()
}
-/// Implement `Node` for a struct.
+/// Turns a struct into an element.
#[proc_macro_attribute]
-pub fn node(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
+pub fn element(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
let item = syn::parse_macro_input!(item as syn::ItemStruct);
- node::node(stream.into(), item)
+ element::element(stream.into(), item)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
diff --git a/src/doc.rs b/src/doc.rs
index f575ff1f..ebdca43e 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -14,7 +14,7 @@ use crate::geom::{
Numeric, Paint, Point, Rel, RgbaColor, Shape, Sides, Size, Stroke, Transform,
};
use crate::image::Image;
-use crate::model::{Content, Introspector, MetaNode, StableId, StyleChain};
+use crate::model::{Content, Location, MetaElem, StyleChain};
use crate::syntax::Span;
/// A finished document with metadata and page frames.
@@ -28,7 +28,7 @@ pub struct Document {
pub author: Vec<EcoString>,
}
-/// A finished layout with elements at fixed positions.
+/// A finished layout with items at fixed positions.
#[derive(Default, Clone, Hash)]
pub struct Frame {
/// The size of the frame.
@@ -36,8 +36,8 @@ pub struct Frame {
/// The baseline of the frame measured from the top. If this is `None`, the
/// frame's implicit baseline is at the bottom.
baseline: Option<Abs>,
- /// The elements composing this layout.
- elements: Arc<Vec<(Point, Element)>>,
+ /// The items composing this layout.
+ items: Arc<Vec<(Point, FrameItem)>>,
}
/// Constructor, accessors and setters.
@@ -48,12 +48,12 @@ impl Frame {
#[track_caller]
pub fn new(size: Size) -> Self {
assert!(size.is_finite());
- Self { size, baseline: None, elements: Arc::new(vec![]) }
+ Self { size, baseline: None, items: Arc::new(vec![]) }
}
- /// Whether the frame contains no elements.
+ /// Whether the frame contains no items.
pub fn is_empty(&self) -> bool {
- self.elements.is_empty()
+ self.items.is_empty()
}
/// The size of the frame.
@@ -109,23 +109,23 @@ impl Frame {
self.size.y - self.baseline()
}
- /// An iterator over the elements inside this frame alongside their
- /// positions relative to the top-left of the frame.
- pub fn elements(&self) -> std::slice::Iter<'_, (Point, Element)> {
- self.elements.iter()
+ /// An iterator over the items inside this frame alongside their positions
+ /// relative to the top-left of the frame.
+ pub fn items(&self) -> std::slice::Iter<'_, (Point, FrameItem)> {
+ self.items.iter()
}
- /// Recover the text inside of the frame and its children.
+ /// Approximately recover the text inside of the frame and its children.
pub fn text(&self) -> EcoString {
let mut text = EcoString::new();
- for (_, element) in self.elements() {
- match element {
- Element::Text(element) => {
- for glyph in &element.glyphs {
+ for (_, item) in self.items() {
+ match item {
+ FrameItem::Text(item) => {
+ for glyph in &item.glyphs {
text.push(glyph.c);
}
}
- Element::Group(group) => text.push_str(&group.frame.text()),
+ FrameItem::Group(group) => text.push_str(&group.frame.text()),
_ => {}
}
}
@@ -133,53 +133,53 @@ impl Frame {
}
}
-/// Insert elements and subframes.
+/// Insert items and subframes.
impl Frame {
/// The layer the next item will be added on. This corresponds to the number
- /// of elements in the frame.
+ /// of items in the frame.
pub fn layer(&self) -> usize {
- self.elements.len()
+ self.items.len()
}
- /// Add an element at a position in the foreground.
- pub fn push(&mut self, pos: Point, element: Element) {
- Arc::make_mut(&mut self.elements).push((pos, element));
+ /// Add an item at a position in the foreground.
+ pub fn push(&mut self, pos: Point, item: FrameItem) {
+ Arc::make_mut(&mut self.items).push((pos, item));
}
/// Add a frame at a position in the foreground.
///
/// Automatically decides whether to inline the frame or to include it as a
- /// group based on the number of elements in it.
+ /// group based on the number of items in it.
pub fn push_frame(&mut self, pos: Point, frame: Frame) {
if self.should_inline(&frame) {
self.inline(self.layer(), pos, frame);
} else {
- self.push(pos, Element::Group(Group::new(frame)));
+ self.push(pos, FrameItem::Group(GroupItem::new(frame)));
}
}
- /// Insert an element at the given layer in the frame.
+ /// Insert an item at the given layer in the frame.
///
/// This panics if the layer is greater than the number of layers present.
#[track_caller]
- pub fn insert(&mut self, layer: usize, pos: Point, element: Element) {
- Arc::make_mut(&mut self.elements).insert(layer, (pos, element));
+ pub fn insert(&mut self, layer: usize, pos: Point, items: FrameItem) {
+ Arc::make_mut(&mut self.items).insert(layer, (pos, items));
}
- /// Add an element at a position in the background.
- pub fn prepend(&mut self, pos: Point, element: Element) {
- Arc::make_mut(&mut self.elements).insert(0, (pos, element));
+ /// Add an item at a position in the background.
+ pub fn prepend(&mut self, pos: Point, item: FrameItem) {
+ Arc::make_mut(&mut self.items).insert(0, (pos, item));
}
- /// Add multiple elements at a position in the background.
+ /// Add multiple items at a position in the background.
///
- /// The first element in the iterator will be the one that is most in the
+ /// The first item in the iterator will be the one that is most in the
/// background.
- pub fn prepend_multiple<I>(&mut self, elements: I)
+ pub fn prepend_multiple<I>(&mut self, items: I)
where
- I: IntoIterator<Item = (Point, Element)>,
+ I: IntoIterator<Item = (Point, FrameItem)>,
{
- Arc::make_mut(&mut self.elements).splice(0..0, elements);
+ Arc::make_mut(&mut self.items).splice(0..0, items);
}
/// Add a frame at a position in the background.
@@ -187,31 +187,31 @@ impl Frame {
if self.should_inline(&frame) {
self.inline(0, pos, frame);
} else {
- self.prepend(pos, Element::Group(Group::new(frame)));
+ self.prepend(pos, FrameItem::Group(GroupItem::new(frame)));
}
}
/// Whether the given frame should be inlined.
fn should_inline(&self, frame: &Frame) -> bool {
- self.elements.is_empty() || frame.elements.len() <= 5
+ self.items.is_empty() || frame.items.len() <= 5
}
/// Inline a frame at the given layer.
fn inline(&mut self, layer: usize, pos: Point, frame: Frame) {
- // Try to just reuse the elements.
- if pos.is_zero() && self.elements.is_empty() {
- self.elements = frame.elements;
+ // Try to just reuse the items.
+ if pos.is_zero() && self.items.is_empty() {
+ self.items = frame.items;
return;
}
- // Try to transfer the elements without adjusting the position.
- // Also try to reuse the elements if the Arc isn't shared.
+ // Try to transfer the items without adjusting the position.
+ // Also try to reuse the items if the Arc isn't shared.
let range = layer..layer;
if pos.is_zero() {
- let sink = Arc::make_mut(&mut self.elements);
- match Arc::try_unwrap(frame.elements) {
- Ok(elements) => {
- sink.splice(range, elements);
+ let sink = Arc::make_mut(&mut self.items);
+ match Arc::try_unwrap(frame.items) {
+ Ok(items) => {
+ sink.splice(range, items);
}
Err(arc) => {
sink.splice(range, arc.iter().cloned());
@@ -220,12 +220,12 @@ impl Frame {
return;
}
- // We must adjust the element positions.
- // But still try to reuse the elements if the Arc isn't shared.
- let sink = Arc::make_mut(&mut self.elements);
- match Arc::try_unwrap(frame.elements) {
- Ok(elements) => {
- sink.splice(range, elements.into_iter().map(|(p, e)| (p + pos, e)));
+ // We have to adjust the item positions.
+ // But still try to reuse the items if the Arc isn't shared.
+ let sink = Arc::make_mut(&mut self.items);
+ match Arc::try_unwrap(frame.items) {
+ Ok(items) => {
+ sink.splice(range, items.into_iter().map(|(p, e)| (p + pos, e)));
}
Err(arc) => {
sink.splice(range, arc.iter().cloned().map(|(p, e)| (p + pos, e)));
@@ -236,12 +236,12 @@ impl Frame {
/// Modify the frame.
impl Frame {
- /// Remove all elements from the frame.
+ /// Remove all items from the frame.
pub fn clear(&mut self) {
- if Arc::strong_count(&self.elements) == 1 {
- Arc::make_mut(&mut self.elements).clear();
+ if Arc::strong_count(&self.items) == 1 {
+ Arc::make_mut(&mut self.items).clear();
} else {
- self.elements = Arc::new(vec![]);
+ self.items = Arc::new(vec![]);
}
}
@@ -264,7 +264,7 @@ impl Frame {
if let Some(baseline) = &mut self.baseline {
*baseline += offset.y;
}
- for (point, _) in Arc::make_mut(&mut self.elements) {
+ for (point, _) in Arc::make_mut(&mut self.items) {
*point += offset;
}
}
@@ -273,12 +273,12 @@ impl Frame {
/// Attach the metadata from this style chain to the frame.
pub fn meta(&mut self, styles: StyleChain, force: bool) {
if force || !self.is_empty() {
- for meta in MetaNode::data_in(styles) {
+ for meta in MetaElem::data_in(styles) {
if matches!(meta, Meta::Hide) {
self.clear();
break;
}
- self.prepend(Point::zero(), Element::Meta(meta, self.size));
+ self.prepend(Point::zero(), FrameItem::Meta(meta, self.size));
}
}
}
@@ -287,7 +287,7 @@ impl Frame {
pub fn fill(&mut self, fill: Paint) {
self.prepend(
Point::zero(),
- Element::Shape(Geometry::Rect(self.size()).filled(fill), Span::detached()),
+ FrameItem::Shape(Geometry::Rect(self.size()).filled(fill), Span::detached()),
);
}
@@ -307,7 +307,7 @@ impl Frame {
self.prepend_multiple(
rounded_rect(size, radius, fill, stroke)
.into_iter()
- .map(|x| (pos, Element::Shape(x, span))),
+ .map(|x| (pos, FrameItem::Shape(x, span))),
)
}
@@ -328,13 +328,13 @@ impl Frame {
/// Wrap the frame's contents in a group and modify that group with `f`.
fn group<F>(&mut self, f: F)
where
- F: FnOnce(&mut Group),
+ F: FnOnce(&mut GroupItem),
{
let mut wrapper = Frame::new(self.size);
wrapper.baseline = self.baseline;
- let mut group = Group::new(std::mem::take(self));
+ let mut group = GroupItem::new(std::mem::take(self));
f(&mut group);
- wrapper.push(Point::zero(), Element::Group(group));
+ wrapper.push(Point::zero(), FrameItem::Group(group));
*self = wrapper;
}
}
@@ -346,7 +346,7 @@ impl Frame {
self.insert(
0,
Point::zero(),
- Element::Shape(
+ FrameItem::Shape(
Geometry::Rect(self.size)
.filled(RgbaColor { a: 100, ..Color::TEAL.to_rgba() }.into()),
Span::detached(),
@@ -355,7 +355,7 @@ impl Frame {
self.insert(
1,
Point::with_y(self.baseline()),
- Element::Shape(
+ FrameItem::Shape(
Geometry::Line(Point::with_x(self.size.x)).stroked(Stroke {
paint: Color::RED.into(),
thickness: Abs::pt(1.0),
@@ -371,7 +371,7 @@ impl Frame {
let radius = Abs::pt(2.0);
self.push(
pos - Point::splat(radius),
- Element::Shape(
+ FrameItem::Shape(
geom::ellipse(Size::splat(2.0 * radius), Some(Color::GREEN.into()), None),
Span::detached(),
),
@@ -382,7 +382,7 @@ impl Frame {
pub fn mark_line(&mut self, y: Abs) {
self.push(
Point::with_y(y),
- Element::Shape(
+ FrameItem::Shape(
Geometry::Line(Point::with_x(self.size.x)).stroked(Stroke {
paint: Color::GREEN.into(),
thickness: Abs::pt(1.0),
@@ -397,18 +397,18 @@ impl Debug for Frame {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Frame ")?;
f.debug_list()
- .entries(self.elements.iter().map(|(_, element)| element))
+ .entries(self.items.iter().map(|(_, item)| item))
.finish()
}
}
/// The building block frames are composed of.
#[derive(Clone, Hash)]
-pub enum Element {
- /// A group of elements.
- Group(Group),
+pub enum FrameItem {
+ /// A subframe with optional transformation and clipping.
+ Group(GroupItem),
/// A run of shaped text.
- Text(Text),
+ Text(TextItem),
/// A geometric shape with optional fill and stroke.
Shape(Shape, Span),
/// An image and its size.
@@ -417,7 +417,7 @@ pub enum Element {
Meta(Meta, Size),
}
-impl Debug for Element {
+impl Debug for FrameItem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Group(group) => group.fmt(f),
@@ -429,9 +429,9 @@ impl Debug for Element {
}
}
-/// A group of elements with optional clipping.
+/// A subframe with optional transformation and clipping.
#[derive(Clone, Hash)]
-pub struct Group {
+pub struct GroupItem {
/// The group's frame.
pub frame: Frame,
/// A transformation to apply to the group.
@@ -440,7 +440,7 @@ pub struct Group {
pub clips: bool,
}
-impl Group {
+impl GroupItem {
/// Create a new group with default settings.
pub fn new(frame: Frame) -> Self {
Self {
@@ -451,7 +451,7 @@ impl Group {
}
}
-impl Debug for Group {
+impl Debug for GroupItem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Group ")?;
self.frame.fmt(f)
@@ -460,7 +460,7 @@ impl Debug for Group {
/// A run of shaped text.
#[derive(Clone, Eq, PartialEq, Hash)]
-pub struct Text {
+pub struct TextItem {
/// The font the glyphs are contained in.
pub font: Font,
/// The font size.
@@ -473,14 +473,14 @@ pub struct Text {
pub glyphs: Vec<Glyph>,
}
-impl Text {
+impl TextItem {
/// The width of the text run.
pub fn width(&self) -> Abs {
self.glyphs.iter().map(|g| g.x_advance).sum::<Em>().at(self.size)
}
}
-impl Debug for Text {
+impl Debug for TextItem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// This is only a rough approxmiation of the source text.
f.write_str("Text(\"")?;
@@ -595,97 +595,73 @@ cast_to_value! {
}
/// Meta information that isn't visible or renderable.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Meta {
- /// Indicates that the content should be hidden.
+ /// An internal or external link to a destination.
+ Link(Destination),
+ /// An identifiable element that produces something within the area this
+ /// metadata is attached to.
+ Elem(Content),
+ /// Indicates that content should be hidden. This variant doesn't appear
+ /// in the final frames as it is removed alongside the content that should
+ /// be hidden.
Hide,
- /// An internal or external link.
- Link(Link),
- /// An identifiable piece of content that produces something within the
- /// area this metadata is attached to.
- Node(Content),
}
cast_from_value! {
Meta: "meta",
}
-impl PartialEq for Meta {
- fn eq(&self, other: &Self) -> bool {
- crate::util::hash128(self) == crate::util::hash128(other)
- }
-}
-
-/// A possibly unresolved link.
-#[derive(Debug, Clone, Hash)]
-pub enum Link {
- /// A fully resolved.
- Dest(Destination),
- /// An unresolved link to a node.
- Node(StableId),
-}
-
-impl Link {
- /// Resolve a destination.
- ///
- /// Needs to lazily provide an introspector.
- pub fn resolve<'a>(
- &self,
- introspector: impl FnOnce() -> &'a Introspector,
- ) -> Destination {
- match self {
- Self::Dest(dest) => dest.clone(),
- Self::Node(id) => Destination::Internal(introspector().location(*id)),
- }
- }
-}
-
/// A link destination.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Destination {
- /// A link to a point on a page.
- Internal(Location),
/// A link to a URL.
Url(EcoString),
+ /// A link to a point on a page.
+ Position(Position),
+ /// An unresolved link to a location in the document.
+ Location(Location),
}
cast_from_value! {
Destination,
- loc: Location => Self::Internal(loc),
- string: EcoString => Self::Url(string),
+ v: EcoString => Self::Url(v),
+ v: Position => Self::Position(v),
+ v: Location => Self::Location(v),
}
cast_to_value! {
v: Destination => match v {
- Destination::Internal(loc) => loc.into(),
- Destination::Url(url) => url.into(),
+ Destination::Url(v) => v.into(),
+ Destination::Position(v) => v.into(),
+ Destination::Location(v) => v.into(),
}
}
-/// A physical location in a document.
+/// A physical position in a document.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Location {
+pub struct Position {
/// The page, starting at 1.
pub page: NonZeroUsize,
/// The exact coordinates on the page (from the top left, as usual).
- pub pos: Point,
+ pub point: Point,
}
cast_from_value! {
- Location,
+ Position,
mut dict: Dict => {
let page = dict.take("page")?.cast()?;
let x: Length = dict.take("x")?.cast()?;
let y: Length = dict.take("y")?.cast()?;
dict.finish(&["page", "x", "y"])?;
- Self { page, pos: Point::new(x.abs, y.abs) }
+ Self { page, point: Point::new(x.abs, y.abs) }
},
}
cast_to_value! {
- v: Location => Value::Dict(dict! {
+ v: Position => Value::Dict(dict! {
"page" => Value::Int(v.page.get() as i64),
- "x" => Value::Length(v.pos.x.into()),
- "y" => Value::Length(v.pos.y.into()),
+ "x" => Value::Length(v.point.x.into()),
+ "y" => Value::Length(v.point.y.into()),
})
}
diff --git a/src/eval/array.rs b/src/eval/array.rs
index fa71ff1a..bebbe809 100644
--- a/src/eval/array.rs
+++ b/src/eval/array.rs
@@ -137,7 +137,7 @@ impl Array {
self.0.contains(value)
}
- /// Return the first matching element.
+ /// Return the first matching item.
pub fn find(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<Value>> {
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
@@ -148,7 +148,7 @@ impl Array {
Ok(None)
}
- /// Return the index of the first matching element.
+ /// Return the index of the first matching item.
pub fn position(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<i64>> {
for (i, item) in self.iter().enumerate() {
let args = Args::new(func.span(), [item.clone()]);
@@ -160,8 +160,8 @@ impl Array {
Ok(None)
}
- /// Return a new array with only those elements for which the function
- /// returns true.
+ /// Return a new array with only those items for which the function returns
+ /// true.
pub fn filter(&self, vm: &mut Vm, func: Func) -> SourceResult<Self> {
let mut kept = EcoVec::new();
for item in self.iter() {
@@ -189,7 +189,7 @@ impl Array {
.collect()
}
- /// Fold all of the array's elements into one with a function.
+ /// Fold all of the array's items into one with a function.
pub fn fold(&self, vm: &mut Vm, init: Value, func: Func) -> SourceResult<Value> {
let mut acc = init;
for item in self.iter() {
@@ -199,7 +199,7 @@ impl Array {
Ok(acc)
}
- /// Whether any element matches.
+ /// Whether any item matches.
pub fn any(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> {
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
@@ -211,7 +211,7 @@ impl Array {
Ok(false)
}
- /// Whether all elements match.
+ /// Whether all items match.
pub fn all(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> {
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 7bf1814f..ef042d6d 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -8,14 +8,12 @@ use comemo::{Prehashed, Track, Tracked, TrackedMut};
use once_cell::sync::Lazy;
use super::{
- cast_to_value, Args, CastInfo, Dict, Eval, Flow, Route, Scope, Scopes, Tracer, Value,
- Vm,
+ cast_to_value, Args, CastInfo, Eval, Flow, Route, Scope, Scopes, Tracer, Value, Vm,
};
-use crate::diag::{bail, SourceResult, StrResult};
-use crate::model::{Introspector, NodeId, Selector, StabilityProvider, StyleMap, Vt};
+use crate::diag::{bail, SourceResult};
+use crate::model::{ElemFunc, Introspector, StabilityProvider, Vt};
use crate::syntax::ast::{self, AstNode, Expr, Ident};
use crate::syntax::{SourceId, Span, SyntaxNode};
-use crate::util::hash128;
use crate::World;
/// An evaluatable function.
@@ -32,8 +30,8 @@ pub struct Func {
enum Repr {
/// A native Rust function.
Native(NativeFunc),
- /// A function for a node.
- Node(NodeId),
+ /// A function for an element.
+ Elem(ElemFunc),
/// A user-defined closure.
Closure(Closure),
/// A nested function with pre-applied arguments.
@@ -45,7 +43,7 @@ impl Func {
pub fn name(&self) -> Option<&str> {
match &**self.repr {
Repr::Native(native) => Some(native.info.name),
- Repr::Node(node) => Some(node.info.name),
+ Repr::Elem(func) => Some(func.info().name),
Repr::Closure(closure) => closure.name.as_deref(),
Repr::With(func, _) => func.name(),
}
@@ -55,7 +53,7 @@ impl Func {
pub fn info(&self) -> Option<&FuncInfo> {
match &**self.repr {
Repr::Native(native) => Some(&native.info),
- Repr::Node(node) => Some(&node.info),
+ Repr::Elem(func) => Some(func.info()),
Repr::With(func, _) => func.info(),
_ => None,
}
@@ -93,8 +91,8 @@ impl Func {
args.finish()?;
Ok(value)
}
- Repr::Node(node) => {
- let value = (node.construct)(vm, &mut args)?;
+ Repr::Elem(func) => {
+ let value = func.construct(vm, &mut args)?;
args.finish()?;
Ok(Value::Content(value))
}
@@ -145,46 +143,13 @@ impl Func {
}
}
- /// Create a selector for this function's node type, filtering by node's
- /// whose [fields](super::Content::field) match the given arguments.
- pub fn where_(self, args: &mut Args) -> StrResult<Selector> {
- let fields = args.to_named();
- args.items.retain(|arg| arg.name.is_none());
- self.select(Some(fields))
- }
-
- /// The node id of this function if it is an element function.
- pub fn id(&self) -> Option<NodeId> {
+ /// Extract the element function, if it is one.
+ pub fn element(&self) -> Option<ElemFunc> {
match **self.repr {
- Repr::Node(id) => Some(id),
+ Repr::Elem(func) => Some(func),
_ => None,
}
}
-
- /// Execute the function's set rule and return the resulting style map.
- pub fn set(&self, mut args: Args) -> SourceResult<StyleMap> {
- Ok(match &**self.repr {
- Repr::Node(node) => {
- let styles = (node.set)(&mut args)?;
- args.finish()?;
- styles
- }
- _ => StyleMap::new(),
- })
- }
-
- /// Create a selector for this function's node type.
- pub fn select(&self, fields: Option<Dict>) -> StrResult<Selector> {
- let Some(id) = self.id() else {
- return Err("this function is not selectable".into());
- };
-
- if id == item!(text_id) {
- Err("to select text, please use a string or regex instead")?;
- }
-
- Ok(Selector::Node(id, fields))
- }
}
impl Debug for Func {
@@ -198,7 +163,7 @@ impl Debug for Func {
impl PartialEq for Func {
fn eq(&self, other: &Self) -> bool {
- hash128(&self.repr) == hash128(&other.repr)
+ self.repr == other.repr
}
}
@@ -211,13 +176,13 @@ impl From<Repr> for Func {
}
}
-impl From<NodeId> for Func {
- fn from(id: NodeId) -> Self {
- Repr::Node(id).into()
+impl From<ElemFunc> for Func {
+ fn from(func: ElemFunc) -> Self {
+ Repr::Elem(func).into()
}
}
-/// A native Rust function.
+/// A Typst function defined by a native Rust function.
pub struct NativeFunc {
/// The function's implementation.
pub func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
diff --git a/src/eval/library.rs b/src/eval/library.rs
index eae342c2..85d5647b 100644
--- a/src/eval/library.rs
+++ b/src/eval/library.rs
@@ -10,7 +10,7 @@ use super::{Args, Dynamic, Module, Value, Vm};
use crate::diag::SourceResult;
use crate::doc::Document;
use crate::geom::{Abs, Dir};
-use crate::model::{Content, Introspector, Label, NodeId, StyleChain, StyleMap, Vt};
+use crate::model::{Content, ElemFunc, Introspector, Label, StyleChain, Styles, Vt};
use crate::syntax::Span;
use crate::util::hash128;
use crate::World;
@@ -23,7 +23,7 @@ pub struct Library {
/// The scope containing definitions available in math mode.
pub math: Module,
/// The default properties for page size, font selection and so on.
- pub styles: StyleMap,
+ pub styles: Styles,
/// Defines which standard library items fulfill which syntactical roles.
pub items: LangItems,
}
@@ -44,9 +44,9 @@ pub struct LangItems {
pub linebreak: fn() -> Content,
/// Plain text without markup.
pub text: fn(text: EcoString) -> Content,
- /// The id of the text node.
- pub text_id: NodeId,
- /// Get the string if this is a text node.
+ /// The text function.
+ pub text_func: ElemFunc,
+ /// Get the string if this is a text element.
pub text_str: fn(&Content) -> Option<EcoString>,
/// A smart quote: `'` or `"`.
pub smart_quote: fn(double: bool) -> Content,
@@ -114,7 +114,7 @@ impl Hash for LangItems {
self.space.hash(state);
self.linebreak.hash(state);
self.text.hash(state);
- self.text_id.hash(state);
+ self.text_func.hash(state);
(self.text_str as usize).hash(state);
self.smart_quote.hash(state);
self.parbreak.hash(state);
@@ -140,13 +140,15 @@ impl Hash for LangItems {
#[doc(hidden)]
pub static LANG_ITEMS: OnceCell<LangItems> = OnceCell::new();
-/// Set the lang items. This is a hack :(
+/// Set the lang items.
///
-/// Passing the lang items everywhere they are needed (especially the text node
-/// related things) is very painful. By storing them globally, in theory, we
-/// break incremental, but only when different sets of lang items are used in
-/// the same program. For this reason, if this function is called multiple
-/// times, the items must be the same.
+/// This is a hack :(
+///
+/// Passing the lang items everywhere they are needed (especially text related
+/// things) is very painful. By storing them globally, in theory, we break
+/// incremental, but only when different sets of lang items are used in the same
+/// program. For this reason, if this function is called multiple times, the
+/// items must be the same (and this is enforced).
pub fn set_lang_items(items: LangItems) {
if let Err(items) = LANG_ITEMS.set(items) {
let first = hash128(LANG_ITEMS.get().unwrap());
diff --git a/src/eval/methods.rs b/src/eval/methods.rs
index 324191ab..72245fb0 100644
--- a/src/eval/methods.rs
+++ b/src/eval/methods.rs
@@ -4,7 +4,7 @@ use ecow::EcoString;
use super::{Args, Str, Value, Vm};
use crate::diag::{At, SourceResult};
-use crate::model::StableId;
+use crate::model::Location;
use crate::syntax::Span;
/// Call a method on a value.
@@ -71,12 +71,12 @@ pub fn call(
},
Value::Content(content) => match method {
- "func" => Value::Func(content.id().into()),
+ "func" => content.func().into(),
"has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)),
"at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(),
- "id" => content
- .stable_id()
- .ok_or("this method can only be called on content returned by query()")
+ "location" => content
+ .location()
+ .ok_or("this method can only be called on content returned by query(..)")
.at(span)?
.into(),
_ => return missing(),
@@ -130,7 +130,16 @@ pub fn call(
Value::Func(func) => match method {
"with" => Value::Func(func.with(args.take())),
- "where" => Value::dynamic(func.where_(&mut args).at(span)?),
+ "where" => {
+ let fields = args.to_named();
+ args.items.retain(|arg| arg.name.is_none());
+ Value::dynamic(
+ func.element()
+ .ok_or("`where()` can only be called on element functions")
+ .at(span)?
+ .where_(fields),
+ )
+ }
_ => return missing(),
},
@@ -141,10 +150,10 @@ pub fn call(
},
Value::Dyn(dynamic) => {
- if let Some(&id) = dynamic.downcast::<StableId>() {
+ if let Some(&location) = dynamic.downcast::<Location>() {
match method {
- "page" => vm.vt.introspector.page(id).into(),
- "location" => vm.vt.introspector.location(id).into(),
+ "page" => vm.vt.introspector.page(location).into(),
+ "position" => vm.vt.introspector.position(location).into(),
_ => return missing(),
}
} else {
@@ -263,7 +272,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
("starts-with", true),
("trim", true),
],
- "content" => &[("func", false), ("has", true), ("at", true), ("id", false)],
+ "content" => &[("func", false), ("has", true), ("at", true), ("location", false)],
"array" => &[
("all", true),
("any", true),
@@ -299,7 +308,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
],
"function" => &[("where", true), ("with", true)],
"arguments" => &[("named", false), ("pos", false)],
- "stable id" => &[("page", false), ("location", false)],
+ "location" => &[("page", false), ("position", false)],
"counter" => &[
("display", true),
("at", true),
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 74c5f0b3..f19e4305 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -29,13 +29,14 @@ pub use self::cast::*;
pub use self::dict::*;
pub use self::func::*;
pub use self::library::*;
-pub use self::methods::*;
pub use self::module::*;
pub use self::scope::*;
pub use self::str::*;
pub use self::symbol::*;
pub use self::value::*;
+pub(crate) use self::methods::methods_on;
+
use std::collections::BTreeMap;
use std::mem;
use std::path::{Path, PathBuf};
@@ -47,11 +48,10 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
-use crate::model::Introspector;
-use crate::model::StabilityProvider;
-use crate::model::Unlabellable;
-use crate::model::Vt;
-use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform};
+use crate::model::{
+ Content, Introspector, Label, Recipe, Selector, StabilityProvider, Styles, Transform,
+ Unlabellable, Vt,
+};
use crate::syntax::ast::AstNode;
use crate::syntax::{
ast, parse_code, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode,
@@ -114,12 +114,12 @@ pub fn eval(
///
/// Everything in the output is associated with the given `span`.
#[comemo::memoize]
-pub fn eval_code_str(
+pub fn eval_string(
world: Tracked<dyn World>,
- text: &str,
+ code: &str,
span: Span,
) -> SourceResult<Value> {
- let mut root = parse_code(text);
+ let mut root = parse_code(code);
root.synthesize(span);
let errors = root.errors();
@@ -290,7 +290,7 @@ impl Route {
}
}
-/// Traces which values existed for the expression with the given span.
+/// Traces which values existed for the expression at a span.
#[derive(Default, Clone)]
pub struct Tracer {
span: Option<Span>,
@@ -377,10 +377,10 @@ fn eval_markup(
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
- if let Some(node) =
+ if let Some(elem) =
seq.iter_mut().rev().find(|node| !node.can::<dyn Unlabellable>())
{
- *node = mem::take(node).labelled(label);
+ *elem = mem::take(elem).labelled(label);
}
}
value => seq.push(value.display().spanned(expr.span())),
@@ -643,7 +643,7 @@ impl Eval for ast::Math {
Ok(Content::sequence(
self.exprs()
.map(|expr| expr.eval_display(vm))
- .collect::<SourceResult<_>>()?,
+ .collect::<SourceResult<Vec<_>>>()?,
))
}
}
@@ -1049,7 +1049,7 @@ impl Eval for ast::FuncCall {
if in_math && !matches!(callee, Value::Func(_)) {
if let Value::Symbol(sym) = &callee {
let c = sym.get();
- if let Some(accent) = combining_accent(c) {
+ if let Some(accent) = Symbol::combining_accent(c) {
let base = args.expect("base")?;
args.finish()?;
return Ok(Value::Content((vm.items.math_accent)(base, accent)));
@@ -1198,17 +1198,25 @@ impl Eval for ast::LetBinding {
}
impl Eval for ast::SetRule {
- type Output = StyleMap;
+ type Output = Styles;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
if let Some(condition) = self.condition() {
if !condition.eval(vm)?.cast::<bool>().at(condition.span())? {
- return Ok(StyleMap::new());
+ return Ok(Styles::new());
}
}
let target = self.target();
- let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
+ let target = target
+ .eval(vm)?
+ .cast::<Func>()
+ .and_then(|func| {
+ func.element().ok_or_else(|| {
+ "only element functions can be used in set rules".into()
+ })
+ })
+ .at(target.span())?;
let args = self.args().eval(vm)?;
Ok(target.set(args)?.spanned(self.span()))
}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index d4338b5c..e241cac5 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -163,7 +163,9 @@ impl Slot {
fn write(&mut self) -> StrResult<&mut Value> {
match self.kind {
Kind::Normal => Ok(&mut self.value),
- Kind::Captured => Err("cannot mutate a captured variable")?,
+ Kind::Captured => {
+ Err("variables from outside the function are read-only and cannot be modified")?
+ }
}
}
}
diff --git a/src/eval/symbol.rs b/src/eval/symbol.rs
index 73c41067..6a199a1d 100644
--- a/src/eval/symbol.rs
+++ b/src/eval/symbol.rs
@@ -1,94 +1,127 @@
use std::cmp::Reverse;
use std::collections::BTreeSet;
use std::fmt::{self, Debug, Display, Formatter, Write};
+use std::sync::Arc;
-use ecow::{EcoString, EcoVec};
+use ecow::EcoString;
use crate::diag::StrResult;
#[doc(inline)]
pub use typst_macros::symbols;
-/// A symbol.
+/// A symbol, possibly with variants.
#[derive(Clone, Eq, PartialEq, Hash)]
-pub struct Symbol {
- repr: Repr,
- modifiers: EcoString,
-}
+pub struct Symbol(Repr);
-/// A collection of symbols.
+/// The internal representation.
#[derive(Clone, Eq, PartialEq, Hash)]
enum Repr {
Single(char),
+ Const(&'static [(&'static str, char)]),
+ Multi(Arc<(List, EcoString)>),
+}
+
+/// A collection of symbols.
+#[derive(Clone, Eq, PartialEq, Hash)]
+enum List {
Static(&'static [(&'static str, char)]),
- Runtime(EcoVec<(EcoString, char)>),
+ Runtime(Box<[(EcoString, char)]>),
}
impl Symbol {
/// Create a new symbol from a single character.
pub const fn new(c: char) -> Self {
- Self { repr: Repr::Single(c), modifiers: EcoString::new() }
+ Self(Repr::Single(c))
}
/// Create a symbol with a static variant list.
#[track_caller]
pub const fn list(list: &'static [(&'static str, char)]) -> Self {
debug_assert!(!list.is_empty());
- Self {
- repr: Repr::Static(list),
- modifiers: EcoString::new(),
- }
+ Self(Repr::Const(list))
}
/// Create a symbol with a runtime variant list.
#[track_caller]
- pub fn runtime(list: EcoVec<(EcoString, char)>) -> Self {
+ pub fn runtime(list: Box<[(EcoString, char)]>) -> Self {
debug_assert!(!list.is_empty());
- Self {
- repr: Repr::Runtime(list),
- modifiers: EcoString::new(),
- }
+ Self(Repr::Multi(Arc::new((List::Runtime(list), EcoString::new()))))
}
/// Get the symbol's text.
pub fn get(&self) -> char {
- match self.repr {
- Repr::Single(c) => c,
- _ => find(self.variants(), &self.modifiers).unwrap(),
+ match &self.0 {
+ Repr::Single(c) => *c,
+ Repr::Const(_) => find(self.variants(), "").unwrap(),
+ Repr::Multi(arc) => find(self.variants(), &arc.1).unwrap(),
}
}
/// Apply a modifier to the symbol.
pub fn modified(mut self, modifier: &str) -> StrResult<Self> {
- if !self.modifiers.is_empty() {
- self.modifiers.push('.');
+ if let Repr::Const(list) = self.0 {
+ self.0 = Repr::Multi(Arc::new((List::Static(list), EcoString::new())));
}
- self.modifiers.push_str(modifier);
- if find(self.variants(), &self.modifiers).is_none() {
- Err("unknown modifier")?
+
+ if let Repr::Multi(arc) = &mut self.0 {
+ let (list, modifiers) = Arc::make_mut(arc);
+ if !modifiers.is_empty() {
+ modifiers.push('.');
+ }
+ modifiers.push_str(modifier);
+ if find(list.variants(), &modifiers).is_some() {
+ return Ok(self);
+ }
}
- Ok(self)
+
+ Err("unknown symbol modifier".into())
}
/// The characters that are covered by this symbol.
pub fn variants(&self) -> impl Iterator<Item = (&str, char)> {
- match &self.repr {
+ match &self.0 {
Repr::Single(c) => Variants::Single(Some(*c).into_iter()),
- Repr::Static(list) => Variants::Static(list.iter()),
- Repr::Runtime(list) => Variants::Runtime(list.iter()),
+ Repr::Const(list) => Variants::Static(list.iter()),
+ Repr::Multi(arc) => arc.0.variants(),
}
}
/// Possible modifiers.
pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
let mut set = BTreeSet::new();
+ let modifiers = match &self.0 {
+ Repr::Multi(arc) => arc.1.as_str(),
+ _ => "",
+ };
for modifier in self.variants().flat_map(|(name, _)| name.split('.')) {
- if !modifier.is_empty() && !contained(&self.modifiers, modifier) {
+ if !modifier.is_empty() && !contained(modifiers, modifier) {
set.insert(modifier);
}
}
set.into_iter()
}
+
+ /// Normalize an accent to a combining one.
+ pub fn combining_accent(c: char) -> Option<char> {
+ Some(match c {
+ '\u{0300}' | '`' => '\u{0300}',
+ '\u{0301}' | '´' => '\u{0301}',
+ '\u{0302}' | '^' | 'ˆ' => '\u{0302}',
+ '\u{0303}' | '~' | '∼' | '˜' => '\u{0303}',
+ '\u{0304}' | '¯' => '\u{0304}',
+ '\u{0305}' | '-' | '‾' | '−' => '\u{0305}',
+ '\u{0306}' | '˘' => '\u{0306}',
+ '\u{0307}' | '.' | '˙' | '⋅' => '\u{0307}',
+ '\u{0308}' | '¨' => '\u{0308}',
+ '\u{030a}' | '∘' | '○' => '\u{030a}',
+ '\u{030b}' | '˝' => '\u{030b}',
+ '\u{030c}' | 'ˇ' => '\u{030c}',
+ '\u{20d6}' | '←' => '\u{20d6}',
+ '\u{20d7}' | '→' | '⟶' => '\u{20d7}',
+ _ => return None,
+ })
+ }
}
impl Debug for Symbol {
@@ -103,6 +136,16 @@ impl Display for Symbol {
}
}
+impl List {
+ /// The characters that are covered by this list.
+ fn variants(&self) -> Variants<'_> {
+ match self {
+ List::Static(list) => Variants::Static(list.iter()),
+ List::Runtime(list) => Variants::Runtime(list.iter()),
+ }
+ }
+}
+
/// Iterator over variants.
enum Variants<'a> {
Single(std::option::IntoIter<char>),
@@ -166,24 +209,3 @@ fn parts(modifiers: &str) -> impl Iterator<Item = &str> {
fn contained(modifiers: &str, m: &str) -> bool {
parts(modifiers).any(|part| part == m)
}
-
-/// Normalize an accent to a combining one.
-pub fn combining_accent(c: char) -> Option<char> {
- Some(match c {
- '\u{0300}' | '`' => '\u{0300}',
- '\u{0301}' | '´' => '\u{0301}',
- '\u{0302}' | '^' | 'ˆ' => '\u{0302}',
- '\u{0303}' | '~' | '∼' | '˜' => '\u{0303}',
- '\u{0304}' | '¯' => '\u{0304}',
- '\u{0305}' | '-' | '‾' | '−' => '\u{0305}',
- '\u{0306}' | '˘' => '\u{0306}',
- '\u{0307}' | '.' | '˙' | '⋅' => '\u{0307}',
- '\u{0308}' | '¨' => '\u{0308}',
- '\u{030a}' | '∘' | '○' => '\u{030a}',
- '\u{030b}' | '˝' => '\u{030b}',
- '\u{030c}' | 'ˇ' => '\u{030c}',
- '\u{20d6}' | '←' => '\u{20d6}',
- '\u{20d7}' | '→' | '⟶' => '\u{20d7}',
- _ => return None,
- })
-}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 61af36f5..ce9c4e0e 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -13,6 +13,7 @@ use super::{
};
use crate::diag::StrResult;
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel};
+use crate::model::Styles;
use crate::syntax::{ast, Span};
/// A computational value.
@@ -48,6 +49,8 @@ pub enum Value {
Label(Label),
/// A content value: `[*Hi* there]`.
Content(Content),
+ // Content styles.
+ Styles(Styles),
/// An array of values: `(1, "hi", 12cm)`.
Array(Array),
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
@@ -101,6 +104,7 @@ impl Value {
Self::Str(_) => Str::TYPE_NAME,
Self::Label(_) => Label::TYPE_NAME,
Self::Content(_) => Content::TYPE_NAME,
+ Self::Styles(_) => Styles::TYPE_NAME,
Self::Array(_) => Array::TYPE_NAME,
Self::Dict(_) => Dict::TYPE_NAME,
Self::Func(_) => Func::TYPE_NAME,
@@ -120,7 +124,7 @@ impl Value {
match self {
Self::Symbol(symbol) => symbol.clone().modified(&field).map(Self::Symbol),
Self::Dict(dict) => dict.at(&field).cloned(),
- Self::Content(content) => content.at(&field).cloned(),
+ Self::Content(content) => content.at(&field),
Self::Module(module) => module.get(&field).cloned(),
v => Err(eco_format!("cannot access fields on type {}", v.type_name())),
}
@@ -188,6 +192,7 @@ impl Debug for Value {
Self::Str(v) => Debug::fmt(v, f),
Self::Label(v) => Debug::fmt(v, f),
Self::Content(v) => Debug::fmt(v, f),
+ Self::Styles(v) => Debug::fmt(v, f),
Self::Array(v) => Debug::fmt(v, f),
Self::Dict(v) => Debug::fmt(v, f),
Self::Func(v) => Debug::fmt(v, f),
@@ -229,6 +234,7 @@ impl Hash for Value {
Self::Str(v) => v.hash(state),
Self::Label(v) => v.hash(state),
Self::Content(v) => v.hash(state),
+ Self::Styles(v) => v.hash(state),
Self::Array(v) => v.hash(state),
Self::Dict(v) => v.hash(state),
Self::Func(v) => v.hash(state),
@@ -400,6 +406,7 @@ primitive! { Content: "content",
Symbol(v) => item!(text)(v.get().into()),
Str(v) => item!(text)(v.into())
}
+primitive! { Styles: "styles", Styles }
primitive! { Array: "array", Array }
primitive! { Dict: "dictionary", Dict }
primitive! { Func: "function", Func }
diff --git a/src/export/pdf/page.rs b/src/export/pdf/page.rs
index 5347d831..f3c81cb8 100644
--- a/src/export/pdf/page.rs
+++ b/src/export/pdf/page.rs
@@ -4,7 +4,7 @@ use pdf_writer::writers::ColorSpace;
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
use super::{deflate, AbsExt, EmExt, PdfContext, RefExt, D65_GRAY, SRGB};
-use crate::doc::{Destination, Element, Frame, Group, Link, Meta, Text};
+use crate::doc::{Destination, Frame, FrameItem, GroupItem, Meta, TextItem};
use crate::font::Font;
use crate::geom::{
self, Abs, Color, Em, Geometry, Numeric, Paint, Point, Ratio, Shape, Size, Stroke,
@@ -110,29 +110,32 @@ fn write_page(ctx: &mut PdfContext, page: Page) {
page_writer.contents(content_id);
let mut annotations = page_writer.annotations();
- for (link, rect) in page.links {
+ for (dest, rect) in page.links {
let mut annotation = annotations.push();
annotation.subtype(AnnotationType::Link).rect(rect);
annotation.border(0.0, 0.0, 0.0, None);
- match link.resolve(|| &ctx.introspector) {
+
+ let pos = match dest {
Destination::Url(uri) => {
annotation
.action()
.action_type(ActionType::Uri)
.uri(Str(uri.as_bytes()));
+ continue;
}
- Destination::Internal(loc) => {
- let index = loc.page.get() - 1;
- let y = (loc.pos.y - Abs::pt(10.0)).max(Abs::zero());
- if let Some(&height) = ctx.page_heights.get(index) {
- annotation
- .action()
- .action_type(ActionType::GoTo)
- .destination_direct()
- .page(ctx.page_refs[index])
- .xyz(loc.pos.x.to_f32(), height - y.to_f32(), None);
- }
- }
+ Destination::Position(pos) => pos,
+ Destination::Location(loc) => ctx.introspector.position(loc),
+ };
+
+ let index = pos.page.get() - 1;
+ let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
+ if let Some(&height) = ctx.page_heights.get(index) {
+ annotation
+ .action()
+ .action_type(ActionType::GoTo)
+ .destination_direct()
+ .page(ctx.page_refs[index])
+ .xyz(pos.point.x.to_f32(), height - y.to_f32(), None);
}
}
@@ -153,7 +156,7 @@ pub struct Page {
/// The page's content stream.
pub content: Content,
/// Links in the PDF coordinate system.
- pub links: Vec<(Link, Rect)>,
+ pub links: Vec<(Destination, Rect)>,
}
/// An exporter for the contents of a single PDF page.
@@ -164,7 +167,7 @@ struct PageContext<'a, 'b> {
state: State,
saves: Vec<State>,
bottom: f32,
- links: Vec<(Link, Rect)>,
+ links: Vec<(Destination, Rect)>,
}
/// A simulated graphics state used to deduplicate graphics state changes and
@@ -283,17 +286,17 @@ impl PageContext<'_, '_> {
/// Encode a frame into the content stream.
fn write_frame(ctx: &mut PageContext, frame: &Frame) {
- for &(pos, ref element) in frame.elements() {
+ for &(pos, ref item) in frame.items() {
let x = pos.x.to_f32();
let y = pos.y.to_f32();
- match element {
- Element::Group(group) => write_group(ctx, pos, group),
- Element::Text(text) => write_text(ctx, x, y, text),
- Element::Shape(shape, _) => write_shape(ctx, x, y, shape),
- Element::Image(image, size, _) => write_image(ctx, x, y, image, *size),
- Element::Meta(meta, size) => match meta {
- Meta::Link(link) => write_link(ctx, pos, link, *size),
- Meta::Node(_) => {}
+ match item {
+ FrameItem::Group(group) => write_group(ctx, pos, group),
+ FrameItem::Text(text) => write_text(ctx, x, y, text),
+ FrameItem::Shape(shape, _) => write_shape(ctx, x, y, shape),
+ FrameItem::Image(image, size, _) => write_image(ctx, x, y, image, *size),
+ FrameItem::Meta(meta, size) => match meta {
+ Meta::Link(dest) => write_link(ctx, pos, dest, *size),
+ Meta::Elem(_) => {}
Meta::Hide => {}
},
}
@@ -301,7 +304,7 @@ fn write_frame(ctx: &mut PageContext, frame: &Frame) {
}
/// Encode a group into the content stream.
-fn write_group(ctx: &mut PageContext, pos: Point, group: &Group) {
+fn write_group(ctx: &mut PageContext, pos: Point, group: &GroupItem) {
let translation = Transform::translate(pos.x, pos.y);
ctx.save_state();
@@ -324,7 +327,7 @@ fn write_group(ctx: &mut PageContext, pos: Point, group: &Group) {
}
/// Encode a text run into the content stream.
-fn write_text(ctx: &mut PageContext, x: f32, y: f32, text: &Text) {
+fn write_text(ctx: &mut PageContext, x: f32, y: f32, text: &TextItem) {
*ctx.parent.languages.entry(text.lang).or_insert(0) += text.glyphs.len();
ctx.parent
.glyph_sets
@@ -422,13 +425,13 @@ fn write_shape(ctx: &mut PageContext, x: f32, y: f32, shape: &Shape) {
fn write_path(ctx: &mut PageContext, x: f32, y: f32, path: &geom::Path) {
for elem in &path.0 {
match elem {
- geom::PathElement::MoveTo(p) => {
+ geom::PathItem::MoveTo(p) => {
ctx.content.move_to(x + p.x.to_f32(), y + p.y.to_f32())
}
- geom::PathElement::LineTo(p) => {
+ geom::PathItem::LineTo(p) => {
ctx.content.line_to(x + p.x.to_f32(), y + p.y.to_f32())
}
- geom::PathElement::CubicTo(p1, p2, p3) => ctx.content.cubic_to(
+ geom::PathItem::CubicTo(p1, p2, p3) => ctx.content.cubic_to(
x + p1.x.to_f32(),
y + p1.y.to_f32(),
x + p2.x.to_f32(),
@@ -436,7 +439,7 @@ fn write_path(ctx: &mut PageContext, x: f32, y: f32, path: &geom::Path) {
x + p3.x.to_f32(),
y + p3.y.to_f32(),
),
- geom::PathElement::ClosePath => ctx.content.close_path(),
+ geom::PathItem::ClosePath => ctx.content.close_path(),
};
}
}
@@ -454,7 +457,7 @@ fn write_image(ctx: &mut PageContext, x: f32, y: f32, image: &Image, size: Size)
}
/// Save a link for later writing in the annotations dictionary.
-fn write_link(ctx: &mut PageContext, pos: Point, link: &Link, size: Size) {
+fn write_link(ctx: &mut PageContext, pos: Point, dest: &Destination, size: Size) {
let mut min_x = Abs::inf();
let mut min_y = Abs::inf();
let mut max_x = -Abs::inf();
@@ -480,5 +483,5 @@ fn write_link(ctx: &mut PageContext, pos: Point, link: &Link, size: Size) {
let y2 = min_y.to_f32();
let rect = Rect::new(x1, y1, x2, y2);
- ctx.links.push((link.clone(), rect));
+ ctx.links.push((dest.clone(), rect));
}
diff --git a/src/export/render.rs b/src/export/render.rs
index 58659b98..11ab5447 100644
--- a/src/export/render.rs
+++ b/src/export/render.rs
@@ -9,9 +9,9 @@ use tiny_skia as sk;
use ttf_parser::{GlyphId, OutlineBuilder};
use usvg::FitTo;
-use crate::doc::{Element, Frame, Group, Meta, Text};
+use crate::doc::{Frame, FrameItem, GroupItem, Meta, TextItem};
use crate::geom::{
- self, Abs, Color, Geometry, Paint, PathElement, Shape, Size, Stroke, Transform,
+ self, Abs, Color, Geometry, Paint, PathItem, Shape, Size, Stroke, Transform,
};
use crate::image::{DecodedImage, Image};
@@ -33,34 +33,34 @@ pub fn render(frame: &Frame, pixel_per_pt: f32, fill: Color) -> sk::Pixmap {
canvas
}
-/// Render all elements in a frame into the canvas.
+/// Render a frame into the canvas.
fn render_frame(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
frame: &Frame,
) {
- for (pos, element) in frame.elements() {
+ for (pos, item) in frame.items() {
let x = pos.x.to_f32();
let y = pos.y.to_f32();
let ts = ts.pre_translate(x, y);
- match element {
- Element::Group(group) => {
+ match item {
+ FrameItem::Group(group) => {
render_group(canvas, ts, mask, group);
}
- Element::Text(text) => {
+ FrameItem::Text(text) => {
render_text(canvas, ts, mask, text);
}
- Element::Shape(shape, _) => {
+ FrameItem::Shape(shape, _) => {
render_shape(canvas, ts, mask, shape);
}
- Element::Image(image, size, _) => {
+ FrameItem::Image(image, size, _) => {
render_image(canvas, ts, mask, image, *size);
}
- Element::Meta(meta, _) => match meta {
+ FrameItem::Meta(meta, _) => match meta {
Meta::Link(_) => {}
- Meta::Node(_) => {}
+ Meta::Elem(_) => {}
Meta::Hide => {}
},
}
@@ -72,7 +72,7 @@ fn render_group(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
- group: &Group,
+ group: &GroupItem,
) {
let ts = ts.pre_concat(group.transform.into());
@@ -114,7 +114,7 @@ fn render_text(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
- text: &Text,
+ text: &TextItem,
) {
let mut x = 0.0;
for glyph in &text.glyphs {
@@ -135,7 +135,7 @@ fn render_svg_glyph(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
_: Option<&sk::ClipMask>,
- text: &Text,
+ text: &TextItem,
id: GlyphId,
) -> Option<()> {
let mut data = text.font.ttf().glyph_svg_image(id)?;
@@ -184,7 +184,7 @@ fn render_bitmap_glyph(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
- text: &Text,
+ text: &TextItem,
id: GlyphId,
) -> Option<()> {
let size = text.size.to_f32();
@@ -208,7 +208,7 @@ fn render_outline_glyph(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
- text: &Text,
+ text: &TextItem,
id: GlyphId,
) -> Option<()> {
let ppem = text.size.to_f32() * ts.sy;
@@ -326,13 +326,13 @@ fn convert_path(path: &geom::Path) -> Option<sk::Path> {
let mut builder = sk::PathBuilder::new();
for elem in &path.0 {
match elem {
- PathElement::MoveTo(p) => {
+ PathItem::MoveTo(p) => {
builder.move_to(p.x.to_f32(), p.y.to_f32());
}
- PathElement::LineTo(p) => {
+ PathItem::LineTo(p) => {
builder.line_to(p.x.to_f32(), p.y.to_f32());
}
- PathElement::CubicTo(p1, p2, p3) => {
+ PathItem::CubicTo(p1, p2, p3) => {
builder.cubic_to(
p1.x.to_f32(),
p1.y.to_f32(),
@@ -342,7 +342,7 @@ fn convert_path(path: &geom::Path) -> Option<sk::Path> {
p3.y.to_f32(),
);
}
- PathElement::ClosePath => {
+ PathItem::ClosePath => {
builder.close();
}
};
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
index c01b21da..eacd6f95 100644
--- a/src/geom/paint.rs
+++ b/src/geom/paint.rs
@@ -236,7 +236,7 @@ impl FromStr for RgbaColor {
fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
if hex_str.chars().any(|c| !c.is_ascii_hexdigit()) {
- return Err("string contains non-hexadecimal letters");
+ return Err("color string contains non-hexadecimal letters");
}
let len = hex_str.len();
@@ -244,7 +244,7 @@ impl FromStr for RgbaColor {
let short = len == 3 || len == 4;
let alpha = len == 4 || len == 8;
if !long && !short {
- return Err("string has wrong length");
+ return Err("color string has wrong length");
}
let mut values: [u8; 4] = [u8::MAX; 4];
@@ -406,10 +406,10 @@ mod tests {
assert_eq!(RgbaColor::from_str(hex), Err(message));
}
- test("a5", "string has wrong length");
- test("12345", "string has wrong length");
- test("f075ff011", "string has wrong length");
- test("hmmm", "string contains non-hexadecimal letters");
- test("14B2AH", "string contains non-hexadecimal letters");
+ test("a5", "color string has wrong length");
+ test("12345", "color string has wrong length");
+ test("f075ff011", "color string has wrong length");
+ test("hmmm", "color string contains non-hexadecimal letters");
+ test("14B2AH", "color string contains non-hexadecimal letters");
}
}
diff --git a/src/geom/path.rs b/src/geom/path.rs
index 3a7c3033..1c5325a3 100644
--- a/src/geom/path.rs
+++ b/src/geom/path.rs
@@ -2,11 +2,11 @@ use super::*;
/// A bezier path.
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
-pub struct Path(pub Vec<PathElement>);
+pub struct Path(pub Vec<PathItem>);
-/// An element in a bezier path.
+/// An item in a bezier path.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum PathElement {
+pub enum PathItem {
MoveTo(Point),
LineTo(Point),
CubicTo(Point, Point, Point),
@@ -32,23 +32,23 @@ impl Path {
path
}
- /// Push a [`MoveTo`](PathElement::MoveTo) element.
+ /// Push a [`MoveTo`](PathItem::MoveTo) item.
pub fn move_to(&mut self, p: Point) {
- self.0.push(PathElement::MoveTo(p));
+ self.0.push(PathItem::MoveTo(p));
}
- /// Push a [`LineTo`](PathElement::LineTo) element.
+ /// Push a [`LineTo`](PathItem::LineTo) item.
pub fn line_to(&mut self, p: Point) {
- self.0.push(PathElement::LineTo(p));
+ self.0.push(PathItem::LineTo(p));
}
- /// Push a [`CubicTo`](PathElement::CubicTo) element.
+ /// Push a [`CubicTo`](PathItem::CubicTo) item.
pub fn cubic_to(&mut self, p1: Point, p2: Point, p3: Point) {
- self.0.push(PathElement::CubicTo(p1, p2, p3));
+ self.0.push(PathItem::CubicTo(p1, p2, p3));
}
- /// Push a [`ClosePath`](PathElement::ClosePath) element.
+ /// Push a [`ClosePath`](PathItem::ClosePath) item.
pub fn close_path(&mut self) {
- self.0.push(PathElement::ClosePath);
+ self.0.push(PathItem::ClosePath);
}
}
diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs
index 68b82b05..27c6c2a4 100644
--- a/src/ide/analyze.rs
+++ b/src/ide/analyze.rs
@@ -81,6 +81,11 @@ pub fn analyze_import(
}
/// Find all labels and details for them.
+///
+/// Returns:
+/// - All labels and descriptions for them, if available
+/// - A split offset: All labels before this offset belong to nodes, all after
+/// belong to a bibliography.
pub fn analyze_labels(
world: &(dyn World + 'static),
frames: &[Frame],
@@ -90,16 +95,16 @@ pub fn analyze_labels(
let items = &world.library().items;
// Labels in the document.
- for node in introspector.all() {
- let Some(label) = node.label() else { continue };
- let details = node
+ for elem in introspector.all() {
+ let Some(label) = elem.label() else { continue };
+ let details = elem
.field("caption")
- .or_else(|| node.field("body"))
+ .or_else(|| elem.field("body"))
.and_then(|field| match field {
Value::Content(content) => Some(content),
_ => None,
})
- .and_then(|content| (items.text_str)(content));
+ .and_then(|content| (items.text_str)(&content));
output.push((label.clone(), details));
}
diff --git a/src/ide/complete.rs b/src/ide/complete.rs
index 4a1f0216..886c1245 100644
--- a/src/ide/complete.rs
+++ b/src/ide/complete.rs
@@ -373,7 +373,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value) {
}
Value::Content(content) => {
for (name, value) in content.fields() {
- ctx.value_completion(Some(name.clone()), value, false, None);
+ ctx.value_completion(Some(name.clone()), &value, false, None);
}
}
Value::Dict(dict) => {
@@ -509,7 +509,7 @@ fn set_rule_completions(ctx: &mut CompletionContext) {
fn show_rule_selector_completions(ctx: &mut CompletionContext) {
ctx.scope_completions(
false,
- |value| matches!(value, Value::Func(func) if func.select(None).is_ok()),
+ |value| matches!(value, Value::Func(func) if func.element().is_some()),
);
ctx.enrich("", ": ");
diff --git a/src/ide/jump.rs b/src/ide/jump.rs
index 17e318a7..d123ac06 100644
--- a/src/ide/jump.rs
+++ b/src/ide/jump.rs
@@ -1,6 +1,8 @@
use std::num::NonZeroUsize;
-use crate::doc::{Destination, Element, Frame, Location, Meta};
+use ecow::EcoString;
+
+use crate::doc::{Destination, Frame, FrameItem, Meta, Position};
use crate::geom::{Geometry, Point, Size};
use crate::model::Introspector;
use crate::syntax::{LinkedNode, Source, SourceId, Span, SyntaxKind};
@@ -11,8 +13,10 @@ use crate::World;
pub enum Jump {
/// Jump to a position in a source file.
Source(SourceId, usize),
- /// Jump to position in the output or to an external URL.
- Dest(Destination),
+ /// Jump to an external URL.
+ Url(EcoString),
+ /// Jump to a point on a page.
+ Position(Position),
}
impl Jump {
@@ -32,20 +36,27 @@ pub fn jump_from_click(
) -> Option<Jump> {
let mut introspector = None;
- // Prefer metadata.
- for (pos, element) in frame.elements() {
- if let Element::Meta(Meta::Link(link), size) = element {
+ // Try to find a link first.
+ for (pos, item) in frame.items() {
+ if let FrameItem::Meta(Meta::Link(dest), size) = item {
if is_in_rect(*pos, *size, click) {
- return Some(Jump::Dest(link.resolve(|| {
- introspector.get_or_insert_with(|| Introspector::new(frames))
- })));
+ return Some(match dest {
+ Destination::Url(url) => Jump::Url(url.clone()),
+ Destination::Position(pos) => Jump::Position(*pos),
+ Destination::Location(loc) => Jump::Position(
+ introspector
+ .get_or_insert_with(|| Introspector::new(frames))
+ .position(*loc),
+ ),
+ });
}
}
}
- for (mut pos, element) in frame.elements().rev() {
- match element {
- Element::Group(group) => {
+ // If there's no link, search for a jump target.
+ for (mut pos, item) in frame.items().rev() {
+ match item {
+ FrameItem::Group(group) => {
// TODO: Handle transformation.
if let Some(span) =
jump_from_click(world, frames, &group.frame, click - pos)
@@ -54,7 +65,7 @@ pub fn jump_from_click(
}
}
- Element::Text(text) => {
+ FrameItem::Text(text) => {
for glyph in &text.glyphs {
if glyph.span.is_detached() {
continue;
@@ -85,14 +96,14 @@ pub fn jump_from_click(
}
}
- Element::Shape(shape, span) => {
+ FrameItem::Shape(shape, span) => {
let Geometry::Rect(size) = shape.geometry else { continue };
if is_in_rect(pos, size, click) {
return Some(Jump::from_span(world, *span));
}
}
- Element::Image(_, size, span) if is_in_rect(pos, *size, click) => {
+ FrameItem::Image(_, size, span) if is_in_rect(pos, *size, click) => {
return Some(Jump::from_span(world, *span));
}
@@ -108,7 +119,7 @@ pub fn jump_from_cursor(
frames: &[Frame],
source: &Source,
cursor: usize,
-) -> Option<Location> {
+) -> Option<Position> {
let node = LinkedNode::new(source.root()).leaf_at(cursor)?;
if node.kind() != SyntaxKind::Text {
return None;
@@ -117,7 +128,10 @@ pub fn jump_from_cursor(
let span = node.span();
for (i, frame) in frames.iter().enumerate() {
if let Some(pos) = find_in_frame(frame, span) {
- return Some(Location { page: NonZeroUsize::new(i + 1).unwrap(), pos });
+ return Some(Position {
+ page: NonZeroUsize::new(i + 1).unwrap(),
+ point: pos,
+ });
}
}
@@ -126,15 +140,15 @@ pub fn jump_from_cursor(
/// Find the position of a span in a frame.
fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> {
- for (mut pos, element) in frame.elements() {
- if let Element::Group(group) = element {
+ for (mut pos, item) in frame.items() {
+ if let FrameItem::Group(group) = item {
// TODO: Handle transformation.
if let Some(point) = find_in_frame(&group.frame, span) {
return Some(point + pos);
}
}
- if let Element::Text(text) = element {
+ if let FrameItem::Text(text) = item {
for glyph in &text.glyphs {
if glyph.span == span {
return Some(pos);
diff --git a/src/lib.rs b/src/lib.rs
index 9dbaf721..392bd7ac 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,12 +9,12 @@
//! The next step is to [evaluate] the markup. This produces a [module],
//! consisting of a scope of values that were exported by the code and
//! [content], a hierarchical, styled representation of what was written in
-//! the source file. The nodes of the content tree are well structured and
+//! the source file. The elements of the content tree are well structured and
//! order-independent and thus much better suited for further processing than
//! the raw markup.
//! - **Typesetting:**
//! Next, the content is [typeset] into a [document] containing one [frame]
-//! per page with elements and fixed positions.
+//! per page with items at fixed positions.
//! - **Exporting:**
//! These frames can finally be exported into an output format (currently
//! supported are [PDF] and [raster images]).
diff --git a/src/model/content.rs b/src/model/content.rs
index 5317236e..b47da62c 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -1,160 +1,144 @@
use std::any::TypeId;
use std::fmt::{self, Debug, Formatter, Write};
-use std::hash::{Hash, Hasher};
-use std::iter::{self, Sum};
-use std::ops::{Add, AddAssign, Deref};
+use std::iter::Sum;
+use std::ops::{Add, AddAssign};
use ecow::{eco_format, EcoString, EcoVec};
-use once_cell::sync::Lazy;
use super::{
- node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap,
- Synthesize,
+ element, Behave, Behaviour, ElemFunc, Element, Fold, Guard, Label, Locatable,
+ Location, Recipe, Style, Styles, Synthesize,
};
use crate::diag::{SourceResult, StrResult};
use crate::doc::Meta;
-use crate::eval::{
- cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
-};
+use crate::eval::{Cast, Str, Value, Vm};
use crate::syntax::Span;
use crate::util::pretty_array_like;
/// Composable representation of styled content.
#[derive(Clone, Hash)]
pub struct Content {
- id: NodeId,
- span: Span,
- fields: EcoVec<(EcoString, Value)>,
- modifiers: EcoVec<Modifier>,
+ func: ElemFunc,
+ attrs: EcoVec<Attr>,
}
-/// Modifiers that can be attached to content.
+/// Attributes that can be attached to content.
#[derive(Debug, Clone, PartialEq, Hash)]
-enum Modifier {
+enum Attr {
+ Span(Span),
+ Field(EcoString),
+ Value(Value),
+ Child(Content),
+ Styles(Styles),
Prepared,
Guard(Guard),
- Id(StableId),
+ Location(Location),
}
impl Content {
- /// Create a content of the given node kind.
- pub fn new(id: NodeId) -> Self {
- Self {
- id,
- span: Span::detached(),
- fields: EcoVec::new(),
- modifiers: EcoVec::new(),
- }
+ /// Create an empty element.
+ pub fn new(func: ElemFunc) -> Self {
+ Self { func, attrs: EcoVec::new() }
}
/// Create empty content.
pub fn empty() -> Self {
- SequenceNode::new(vec![]).pack()
+ Self::new(SequenceElem::func())
}
- /// Create a new sequence node from multiples nodes.
- pub fn sequence(seq: Vec<Self>) -> Self {
- match seq.as_slice() {
- [_] => seq.into_iter().next().unwrap(),
- _ => SequenceNode::new(seq).pack(),
- }
+ /// Create a new sequence element from multiples elements.
+ pub fn sequence(iter: impl IntoIterator<Item = Self>) -> Self {
+ let mut iter = iter.into_iter();
+ let Some(first) = iter.next() else { return Self::empty() };
+ let Some(second) = iter.next() else { return first };
+ let mut content = Content::empty();
+ content.attrs.push(Attr::Child(first));
+ content.attrs.push(Attr::Child(second));
+ content.attrs.extend(iter.map(Attr::Child));
+ content
}
- /// The id of the contained node.
- pub fn id(&self) -> NodeId {
- self.id
+ /// The element function of the contained content.
+ pub fn func(&self) -> ElemFunc {
+ self.func
}
- /// Whether the content is empty.
+ /// Whether the content is an empty sequence.
pub fn is_empty(&self) -> bool {
- self.to::<SequenceNode>()
- .map_or(false, |seq| seq.children().is_empty())
+ self.is::<SequenceElem>() && self.attrs.is_empty()
}
- /// Whether the contained node is of type `T`.
- pub fn is<T>(&self) -> bool
- where
- T: Node + 'static,
- {
- self.id == NodeId::of::<T>()
+ /// Whether the contained element is of type `T`.
+ pub fn is<T: Element>(&self) -> bool {
+ self.func == T::func()
}
- /// Cast to `T` if the contained node is of type `T`.
- pub fn to<T>(&self) -> Option<&T>
- where
- T: Node + 'static,
- {
- self.is::<T>().then(|| unsafe { std::mem::transmute(self) })
+ /// Cast to `T` if the contained element is of type `T`.
+ pub fn to<T: Element>(&self) -> Option<&T> {
+ T::unpack(self)
}
- /// Whether this content has the given capability.
+ /// Access the children if this is a sequence.
+ pub fn to_sequence(&self) -> Option<impl Iterator<Item = &Self>> {
+ if !self.is::<SequenceElem>() {
+ return None;
+ }
+ Some(self.attrs.iter().filter_map(Attr::child))
+ }
+
+ /// Access the child and styles.
+ pub fn to_styled(&self) -> Option<(&Content, &Styles)> {
+ if !self.is::<StyledElem>() {
+ return None;
+ }
+ let child = self.attrs.iter().find_map(Attr::child)?;
+ let styles = self.attrs.iter().find_map(Attr::styles)?;
+ Some((child, styles))
+ }
+
+ /// Whether the contained element has the given capability.
pub fn can<C>(&self) -> bool
where
C: ?Sized + 'static,
{
- (self.id.0.vtable)(TypeId::of::<C>()).is_some()
+ (self.func.0.vtable)(TypeId::of::<C>()).is_some()
}
- /// Cast to a trait object if this content has the given capability.
+ /// Cast to a trait object if the contained element has the given
+ /// capability.
pub fn with<C>(&self) -> Option<&C>
where
C: ?Sized + 'static,
{
- let vtable = (self.id.0.vtable)(TypeId::of::<C>())?;
+ let vtable = (self.func.0.vtable)(TypeId::of::<C>())?;
let data = self as *const Self as *const ();
Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
}
- /// Cast to a trait object if this content has the given capability.
+ /// Cast to a mutable trait object if the contained element has the given
+ /// capability.
pub fn with_mut<C>(&mut self) -> Option<&mut C>
where
C: ?Sized + 'static,
{
- let vtable = (self.id.0.vtable)(TypeId::of::<C>())?;
+ let vtable = (self.func.0.vtable)(TypeId::of::<C>())?;
let data = self as *mut Self as *mut ();
Some(unsafe { &mut *crate::util::fat::from_raw_parts_mut(data, vtable) })
}
- /// The node's span.
+ /// The content's span.
pub fn span(&self) -> Span {
- self.span
+ self.attrs.iter().find_map(Attr::span).unwrap_or(Span::detached())
}
/// Attach a span to the content if it doesn't already have one.
pub fn spanned(mut self, span: Span) -> Self {
- if self.span.is_detached() {
- self.span = span;
+ if self.span().is_detached() {
+ self.attrs.push(Attr::Span(span));
}
self
}
- /// Access a field on the content.
- pub fn field(&self, name: &str) -> Option<&Value> {
- self.fields
- .iter()
- .find(|(field, _)| field == name)
- .map(|(_, value)| value)
- }
-
- /// Try to access a field on the content as a specified type.
- pub fn cast_field<T: Cast>(&self, name: &str) -> Option<T> {
- match self.field(name) {
- Some(value) => value.clone().cast().ok(),
- None => None,
- }
- }
-
- /// Expect a field on the content to exist as a specified type.
- #[track_caller]
- pub fn expect_field<T: Cast>(&self, name: &str) -> T {
- self.field(name).unwrap().clone().cast().unwrap()
- }
-
- /// List all fields on the content.
- pub fn fields(&self) -> &[(EcoString, Value)] {
- &self.fields
- }
-
/// Attach a field to the content.
pub fn with_field(
mut self,
@@ -168,26 +152,97 @@ impl Content {
/// Attach a field to the content.
pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl Into<Value>) {
let name = name.into();
- if let Some(i) = self.fields.iter().position(|(field, _)| *field == name) {
- self.fields.make_mut()[i] = (name, value.into());
+ if let Some(i) = self.attrs.iter().position(|attr| match attr {
+ Attr::Field(field) => *field == name,
+ _ => false,
+ }) {
+ self.attrs.make_mut()[i + 1] = Attr::Value(value.into());
+ } else {
+ self.attrs.push(Attr::Field(name));
+ self.attrs.push(Attr::Value(value.into()));
+ }
+ }
+
+ /// Access a field on the content.
+ pub fn field(&self, name: &str) -> Option<Value> {
+ if let Some(iter) = self.to_sequence() {
+ (name == "children")
+ .then(|| Value::Array(iter.cloned().map(Value::Content).collect()))
+ } else if let Some((child, _)) = self.to_styled() {
+ (name == "child").then(|| Value::Content(child.clone()))
} else {
- self.fields.push((name, value.into()));
+ self.field_ref(name).cloned()
+ }
+ }
+
+ /// Access a field on the content by reference.
+ ///
+ /// Does not include synthesized fields for sequence and styled elements.
+ pub fn field_ref(&self, name: &str) -> Option<&Value> {
+ self.fields_ref()
+ .find(|&(field, _)| field == name)
+ .map(|(_, value)| value)
+ }
+
+ /// Iter over all fields on the content.
+ ///
+ /// Does not include synthesized fields for sequence and styled elements.
+ pub fn fields(&self) -> impl Iterator<Item = (&EcoString, Value)> {
+ static CHILD: EcoString = EcoString::inline("child");
+ static CHILDREN: EcoString = EcoString::inline("children");
+
+ let option = if let Some(iter) = self.to_sequence() {
+ Some((&CHILDREN, Value::Array(iter.cloned().map(Value::Content).collect())))
+ } else if let Some((child, _)) = self.to_styled() {
+ Some((&CHILD, Value::Content(child.clone())))
+ } else {
+ None
+ };
+
+ self.fields_ref()
+ .map(|(name, value)| (name, value.clone()))
+ .chain(option)
+ }
+
+ /// Iter over all fields on the content.
+ ///
+ /// Does not include synthesized fields for sequence and styled elements.
+ pub fn fields_ref(&self) -> impl Iterator<Item = (&EcoString, &Value)> {
+ let mut iter = self.attrs.iter();
+ std::iter::from_fn(move || {
+ let field = iter.find_map(Attr::field)?;
+ let value = iter.next()?.value()?;
+ Some((field, value))
+ })
+ }
+
+ /// Try to access a field on the content as a specified type.
+ pub fn cast_field<T: Cast>(&self, name: &str) -> Option<T> {
+ match self.field(name) {
+ Some(value) => value.cast().ok(),
+ None => None,
}
}
+ /// Expect a field on the content to exist as a specified type.
+ #[track_caller]
+ pub fn expect_field<T: Cast>(&self, name: &str) -> T {
+ self.field(name).unwrap().cast().unwrap()
+ }
+
/// Whether the content has the specified field.
pub fn has(&self, field: &str) -> bool {
self.field(field).is_some()
}
/// Borrow the value of the given field.
- pub fn at(&self, field: &str) -> StrResult<&Value> {
+ pub fn at(&self, field: &str) -> StrResult<Value> {
self.field(field).ok_or_else(|| missing_field(field))
}
/// The content's label.
pub fn label(&self) -> Option<&Label> {
- match self.field("label")? {
+ match self.field_ref("label")? {
Value::Label(label) => Some(label),
_ => None,
}
@@ -199,20 +254,33 @@ impl Content {
}
/// Style this content with a style entry.
- pub fn styled(self, style: impl Into<Style>) -> Self {
- self.styled_with_map(style.into().into())
+ pub fn styled(mut self, style: impl Into<Style>) -> Self {
+ if self.is::<StyledElem>() {
+ let prev =
+ self.attrs.make_mut().iter_mut().find_map(Attr::styles_mut).unwrap();
+ prev.apply_one(style.into());
+ self
+ } else {
+ self.styled_with_map(style.into().into())
+ }
}
/// Style this content with a full style map.
- pub fn styled_with_map(self, styles: StyleMap) -> Self {
+ pub fn styled_with_map(mut self, styles: Styles) -> Self {
if styles.is_empty() {
+ return self;
+ }
+
+ if self.is::<StyledElem>() {
+ let prev =
+ self.attrs.make_mut().iter_mut().find_map(Attr::styles_mut).unwrap();
+ prev.apply(styles);
self
- } else if let Some(styled) = self.to::<StyledNode>() {
- let mut map = styled.styles();
- map.apply(styles);
- StyledNode::new(map, styled.body()).pack()
} else {
- StyledNode::new(styles, self).pack()
+ let mut content = Content::new(StyledElem::func());
+ content.attrs.push(Attr::Child(self));
+ content.attrs.push(Attr::Styles(styles));
+ content
}
}
@@ -221,7 +289,7 @@ impl Content {
if recipe.selector.is_none() {
recipe.apply_vm(vm, self)
} else {
- Ok(self.styled(Style::Recipe(recipe)))
+ Ok(self.styled(recipe))
}
}
@@ -232,35 +300,34 @@ impl Content {
Ok(Self::sequence(vec![self.clone(); count]))
}
-}
-#[doc(hidden)]
-impl Content {
/// Disable a show rule recipe.
- pub fn guarded(mut self, id: Guard) -> Self {
- self.modifiers.push(Modifier::Guard(id));
+ pub fn guarded(mut self, guard: Guard) -> Self {
+ self.attrs.push(Attr::Guard(guard));
self
}
- /// Whether no show rule was executed for this node so far.
- pub(super) fn is_pristine(&self) -> bool {
- !self
- .modifiers
- .iter()
- .any(|modifier| matches!(modifier, Modifier::Guard(_)))
+ /// Check whether a show rule recipe is disabled.
+ pub fn is_guarded(&self, guard: Guard) -> bool {
+ self.attrs.contains(&Attr::Guard(guard))
}
- /// Check whether a show rule recipe is disabled.
- pub(super) fn is_guarded(&self, id: Guard) -> bool {
- self.modifiers.contains(&Modifier::Guard(id))
+ /// Whether no show rule was executed for this content so far.
+ pub fn is_pristine(&self) -> bool {
+ !self.attrs.iter().any(|modifier| matches!(modifier, Attr::Guard(_)))
}
- /// Whether this node was prepared.
+ /// Whether this content has already been prepared.
pub fn is_prepared(&self) -> bool {
- self.modifiers.contains(&Modifier::Prepared)
+ self.attrs.contains(&Attr::Prepared)
}
- /// Whether the node needs to be realized specially.
+ /// Mark this content as prepared.
+ pub fn mark_prepared(&mut self) {
+ self.attrs.push(Attr::Prepared);
+ }
+
+ /// Whether the content needs to be realized specially.
pub fn needs_preparation(&self) -> bool {
(self.can::<dyn Locatable>()
|| self.can::<dyn Synthesize>()
@@ -268,37 +335,23 @@ impl Content {
&& !self.is_prepared()
}
- /// Mark this content as prepared.
- pub fn mark_prepared(&mut self) {
- self.modifiers.push(Modifier::Prepared);
- }
-
- /// Attach a stable id to this content.
- pub fn set_stable_id(&mut self, id: StableId) {
- self.modifiers.push(Modifier::Id(id));
- }
-
- /// This content's stable identifier.
- pub fn stable_id(&self) -> Option<StableId> {
- self.modifiers.iter().find_map(|modifier| match modifier {
- Modifier::Id(id) => Some(*id),
+ /// This content's location in the document flow.
+ pub fn location(&self) -> Option<Location> {
+ self.attrs.iter().find_map(|modifier| match modifier {
+ Attr::Location(location) => Some(*location),
_ => None,
})
}
- /// Copy the modifiers from another piece of content.
- pub(super) fn copy_modifiers(&mut self, from: &Content) {
- self.span = from.span;
- self.modifiers = from.modifiers.clone();
- if let Some(label) = from.label() {
- self.push_field("label", label.clone())
- }
+ /// Attach a location to this content.
+ pub fn set_location(&mut self, location: Location) {
+ self.attrs.push(Attr::Location(location));
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let name = self.id.name;
+ let name = self.func.name();
if let Some(text) = item!(text_str)(self) {
f.write_char('[')?;
f.write_str(&text)?;
@@ -308,12 +361,15 @@ impl Debug for Content {
return f.write_str("[ ]");
}
- let pieces: Vec<_> = self
- .fields
- .iter()
+ let mut pieces: Vec<_> = self
+ .fields()
.map(|(name, value)| eco_format!("{name}: {value:?}"))
.collect();
+ if self.is::<StyledElem>() {
+ pieces.push(EcoString::from(".."));
+ }
+
f.write_str(name)?;
f.write_str(&pretty_array_like(&pieces, false))
}
@@ -327,31 +383,36 @@ impl Default for Content {
impl PartialEq for Content {
fn eq(&self, other: &Self) -> bool {
- self.id == other.id
- && self.fields.len() == other.fields.len()
- && self
- .fields
- .iter()
- .all(|(name, value)| other.field(name) == Some(value))
+ if let (Some(left), Some(right)) = (self.to_sequence(), other.to_sequence()) {
+ left.eq(right)
+ } else if let (Some(left), Some(right)) = (self.to_styled(), other.to_styled()) {
+ left == right
+ } else {
+ self.func == other.func && self.fields_ref().eq(other.fields_ref())
+ }
}
}
impl Add for Content {
type Output = Self;
- fn add(self, rhs: Self) -> Self::Output {
- let lhs = self;
- let seq = match (lhs.to::<SequenceNode>(), rhs.to::<SequenceNode>()) {
- (Some(lhs), Some(rhs)) => {
- lhs.children().into_iter().chain(rhs.children()).collect()
+ fn add(self, mut rhs: Self) -> Self::Output {
+ let mut lhs = self;
+ match (lhs.is::<SequenceElem>(), rhs.is::<SequenceElem>()) {
+ (true, true) => {
+ lhs.attrs.extend(rhs.attrs);
+ lhs
}
- (Some(lhs), None) => {
- lhs.children().into_iter().chain(iter::once(rhs)).collect()
+ (true, false) => {
+ lhs.attrs.push(Attr::Child(rhs));
+ lhs
}
- (None, Some(rhs)) => iter::once(lhs).chain(rhs.children()).collect(),
- (None, None) => vec![lhs, rhs],
- };
- SequenceNode::new(seq).pack()
+ (false, true) => {
+ rhs.attrs.insert(0, Attr::Child(lhs));
+ rhs
+ }
+ (false, false) => Self::sequence([lhs, rhs]),
+ }
}
}
@@ -363,154 +424,77 @@ impl AddAssign for Content {
impl Sum for Content {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self::sequence(iter.collect())
+ Self::sequence(iter)
}
}
-/// A constructable, stylable content node.
-pub trait Node: Construct + Set + Sized + 'static {
- /// The node's ID.
- fn id() -> NodeId;
-
- /// Pack a node into type-erased content.
- fn pack(self) -> Content;
-}
-
-/// A unique identifier for a node.
-#[derive(Copy, Clone)]
-pub struct NodeId(pub &'static NodeMeta);
-
-impl NodeId {
- /// Get the id of a node.
- pub fn of<T: Node>() -> Self {
- T::id()
+impl Attr {
+ fn child(&self) -> Option<&Content> {
+ match self {
+ Self::Child(child) => Some(child),
+ _ => None,
+ }
}
-}
-impl Debug for NodeId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(self.name)
+ fn styles(&self) -> Option<&Styles> {
+ match self {
+ Self::Styles(styles) => Some(styles),
+ _ => None,
+ }
}
-}
-impl Hash for NodeId {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_usize(self.0 as *const _ as usize);
+ fn styles_mut(&mut self) -> Option<&mut Styles> {
+ match self {
+ Self::Styles(styles) => Some(styles),
+ _ => None,
+ }
}
-}
-
-impl Eq for NodeId {}
-impl PartialEq for NodeId {
- fn eq(&self, other: &Self) -> bool {
- std::ptr::eq(self.0, other.0)
+ fn field(&self) -> Option<&EcoString> {
+ match self {
+ Self::Field(field) => Some(field),
+ _ => None,
+ }
}
-}
-
-impl Deref for NodeId {
- type Target = NodeMeta;
- fn deref(&self) -> &Self::Target {
- self.0
+ fn value(&self) -> Option<&Value> {
+ match self {
+ Self::Value(value) => Some(value),
+ _ => None,
+ }
}
-}
-
-cast_from_value! {
- NodeId,
- v: Func => v.id().ok_or("this function is not an element")?
-}
-
-cast_to_value! {
- v: NodeId => Value::Func(v.into())
-}
-
-/// Static node for a node.
-pub struct NodeMeta {
- /// The node's name.
- pub name: &'static str,
- /// The node's vtable for caspability dispatch.
- pub vtable: fn(of: TypeId) -> Option<*const ()>,
- /// The node's constructor.
- pub construct: fn(&Vm, &mut Args) -> SourceResult<Content>,
- /// The node's set rule.
- pub set: fn(&mut Args) -> SourceResult<StyleMap>,
- /// Details about the function.
- pub info: Lazy<FuncInfo>,
-}
-
-/// A node's constructor function.
-pub trait Construct {
- /// Construct a node from the arguments.
- ///
- /// This is passed only the arguments that remain after execution of the
- /// node's set rule.
- fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content>;
-}
-
-/// A node's set rule.
-pub trait Set {
- /// Parse relevant arguments into style properties for this node.
- fn set(args: &mut Args) -> SourceResult<StyleMap>;
-}
-
-/// Indicates that a node cannot be labelled.
-pub trait Unlabellable {}
-/// A label for a node.
-#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Label(pub EcoString);
-
-impl Debug for Label {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "<{}>", self.0)
+ fn span(&self) -> Option<Span> {
+ match self {
+ Self::Span(span) => Some(*span),
+ _ => None,
+ }
}
}
-/// A sequence of nodes.
-///
-/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in
-/// Typst, the two text nodes are combined into a single sequence node.
-///
/// Display: Sequence
/// Category: special
-#[node]
-pub struct SequenceNode {
- #[variadic]
- pub children: Vec<Content>,
-}
+#[element]
+struct SequenceElem {}
-/// A node with applied styles.
-///
-/// Display: Styled
+/// Display: Sequence
/// Category: special
-#[node]
-pub struct StyledNode {
- /// The styles.
- #[required]
- pub styles: StyleMap,
-
- /// The styled content.
- #[required]
- pub body: Content,
-}
-
-cast_from_value! {
- StyleMap: "style map",
-}
+#[element]
+struct StyledElem {}
-/// Host for metadata.
+/// Hosts metadata and ensures metadata is produced even for empty elements.
///
/// Display: Meta
/// Category: special
-#[node(Behave)]
-pub struct MetaNode {
+#[element(Behave)]
+pub struct MetaElem {
/// Metadata that should be attached to all elements affected by this style
/// property.
#[fold]
pub data: Vec<Meta>,
}
-impl Behave for MetaNode {
+impl Behave for MetaElem {
fn behaviour(&self) -> Behaviour {
Behaviour::Ignorant
}
diff --git a/src/model/element.rs b/src/model/element.rs
new file mode 100644
index 00000000..e25b22b4
--- /dev/null
+++ b/src/model/element.rs
@@ -0,0 +1,145 @@
+use std::any::TypeId;
+use std::fmt::{self, Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use ecow::EcoString;
+use once_cell::sync::Lazy;
+
+use super::{Content, Selector, Styles};
+use crate::diag::SourceResult;
+use crate::eval::{
+ cast_from_value, cast_to_value, Args, Dict, Func, FuncInfo, Value, Vm,
+};
+
+/// A document element.
+pub trait Element: Construct + Set + Sized + 'static {
+ /// Pack the element into type-erased content.
+ fn pack(self) -> Content;
+
+ /// Extract this element from type-erased content.
+ fn unpack(content: &Content) -> Option<&Self>;
+
+ /// The element's function.
+ fn func() -> ElemFunc;
+}
+
+/// An element's constructor function.
+pub trait Construct {
+ /// Construct an element from the arguments.
+ ///
+ /// This is passed only the arguments that remain after execution of the
+ /// element's set rule.
+ fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>;
+}
+
+/// An element's set rule.
+pub trait Set {
+ /// Parse relevant arguments into style properties for this element.
+ fn set(args: &mut Args) -> SourceResult<Styles>;
+}
+
+/// An element's function.
+#[derive(Copy, Clone)]
+pub struct ElemFunc(pub(super) &'static NativeElemFunc);
+
+impl ElemFunc {
+ /// The function's name.
+ pub fn name(self) -> &'static str {
+ self.0.name
+ }
+
+ /// Apply the given arguments to the function.
+ pub fn with(self, args: Args) -> Func {
+ Func::from(self).with(args)
+ }
+
+ /// Extract details about the function.
+ pub fn info(&self) -> &'static FuncInfo {
+ &self.0.info
+ }
+
+ /// Construct an element.
+ pub fn construct(self, vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
+ (self.0.construct)(vm, args)
+ }
+
+ /// Create a selector for elements of this function.
+ pub fn select(self) -> Selector {
+ Selector::Elem(self, None)
+ }
+
+ /// Create a selector for elements of this function, filtering for those
+ /// whose [fields](super::Content::field) match the given arguments.
+ pub fn where_(self, fields: Dict) -> Selector {
+ Selector::Elem(self, Some(fields))
+ }
+
+ /// Execute the set rule for the element and return the resulting style map.
+ pub fn set(self, mut args: Args) -> SourceResult<Styles> {
+ let styles = (self.0.set)(&mut args)?;
+ args.finish()?;
+ Ok(styles)
+ }
+}
+
+impl Debug for ElemFunc {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.pad(self.name())
+ }
+}
+
+impl Hash for ElemFunc {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_usize(self.0 as *const _ as usize);
+ }
+}
+
+impl Eq for ElemFunc {}
+
+impl PartialEq for ElemFunc {
+ fn eq(&self, other: &Self) -> bool {
+ std::ptr::eq(self.0, other.0)
+ }
+}
+
+cast_from_value! {
+ ElemFunc,
+ v: Func => v.element().ok_or("expected element function")?,
+}
+
+cast_to_value! {
+ v: ElemFunc => Value::Func(v.into())
+}
+
+impl From<&'static NativeElemFunc> for ElemFunc {
+ fn from(native: &'static NativeElemFunc) -> Self {
+ Self(native)
+ }
+}
+
+/// An element function backed by a Rust type.
+pub struct NativeElemFunc {
+ /// The element's name.
+ pub name: &'static str,
+ /// The element's vtable for capability dispatch.
+ pub vtable: fn(of: TypeId) -> Option<*const ()>,
+ /// The element's constructor.
+ pub construct: fn(&mut Vm, &mut Args) -> SourceResult<Content>,
+ /// The element's set rule.
+ pub set: fn(&mut Args) -> SourceResult<Styles>,
+ /// Details about the function.
+ pub info: Lazy<FuncInfo>,
+}
+
+/// A label for an element.
+#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Label(pub EcoString);
+
+impl Debug for Label {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "<{}>", self.0)
+ }
+}
+
+/// Indicates that an element cannot be labelled.
+pub trait Unlabellable {}
diff --git a/src/model/introspect.rs b/src/model/introspect.rs
new file mode 100644
index 00000000..35f0e628
--- /dev/null
+++ b/src/model/introspect.rs
@@ -0,0 +1,170 @@
+use std::fmt::{self, Debug, Formatter};
+use std::hash::Hash;
+use std::num::NonZeroUsize;
+
+use super::{Content, Selector};
+use crate::doc::{Frame, FrameItem, Meta, Position};
+use crate::eval::cast_from_value;
+use crate::geom::{Point, Transform};
+use crate::util::NonZeroExt;
+
+/// Stably identifies a location in the document across multiple layout passes.
+///
+/// This struct is created by [`StabilityProvider::locate`].
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Location(u128, usize, usize);
+
+impl Location {
+ /// Produce a variant of this location.
+ pub fn variant(self, n: usize) -> Self {
+ Self(self.0, self.1, n)
+ }
+}
+
+impl Debug for Location {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.pad("..")
+ }
+}
+
+cast_from_value! {
+ Location: "location",
+}
+
+/// Provides stable identities to elements.
+#[derive(Clone)]
+pub struct StabilityProvider {
+ hashes: Vec<u128>,
+ checkpoints: Vec<usize>,
+}
+
+impl StabilityProvider {
+ /// Create a new stability provider.
+ pub fn new() -> Self {
+ Self { hashes: vec![], checkpoints: vec![] }
+ }
+}
+
+#[comemo::track]
+impl StabilityProvider {
+ /// Produce a stable identifier for this call site.
+ pub fn locate(&mut self, hash: u128) -> Location {
+ let count = self.hashes.iter().filter(|&&prev| prev == hash).count();
+ self.hashes.push(hash);
+ Location(hash, count, 0)
+ }
+
+ /// Create a checkpoint of the state that can be restored.
+ pub fn save(&mut self) {
+ self.checkpoints.push(self.hashes.len());
+ }
+
+ /// Restore the last checkpoint.
+ pub fn restore(&mut self) {
+ if let Some(checkpoint) = self.checkpoints.pop() {
+ self.hashes.truncate(checkpoint);
+ }
+ }
+}
+
+/// Can be queried for elements and their positions.
+pub struct Introspector {
+ pages: usize,
+ elems: Vec<(Content, Position)>,
+}
+
+impl Introspector {
+ /// Create a new introspector.
+ pub fn new(frames: &[Frame]) -> Self {
+ let mut introspector = Self { pages: frames.len(), elems: vec![] };
+ for (i, frame) in frames.iter().enumerate() {
+ let page = NonZeroUsize::new(1 + i).unwrap();
+ introspector.extract(frame, page, Transform::identity());
+ }
+ introspector
+ }
+
+ /// Iterate over all elements.
+ pub fn all(&self) -> impl Iterator<Item = &Content> {
+ self.elems.iter().map(|(elem, _)| elem)
+ }
+
+ /// Extract metadata from a frame.
+ fn extract(&mut self, frame: &Frame, page: NonZeroUsize, ts: Transform) {
+ for (pos, item) in frame.items() {
+ match item {
+ FrameItem::Group(group) => {
+ let ts = ts
+ .pre_concat(Transform::translate(pos.x, pos.y))
+ .pre_concat(group.transform);
+ self.extract(&group.frame, page, ts);
+ }
+ FrameItem::Meta(Meta::Elem(content), _)
+ if !self
+ .elems
+ .iter()
+ .any(|(prev, _)| prev.location() == content.location()) =>
+ {
+ let pos = pos.transform(ts);
+ self.elems.push((content.clone(), Position { page, point: pos }));
+ }
+ _ => {}
+ }
+ }
+ }
+}
+
+#[comemo::track]
+impl Introspector {
+ /// Whether this introspector is not yet initialized.
+ pub fn init(&self) -> bool {
+ self.pages > 0
+ }
+
+ /// Query for all matching elements.
+ pub fn query(&self, selector: Selector) -> Vec<Content> {
+ self.all().filter(|elem| selector.matches(elem)).cloned().collect()
+ }
+
+ /// Query for all matching element up to the given location.
+ pub fn query_before(&self, selector: Selector, location: Location) -> Vec<Content> {
+ let mut matches = vec![];
+ for elem in self.all() {
+ if selector.matches(elem) {
+ matches.push(elem.clone());
+ }
+ if elem.location() == Some(location) {
+ break;
+ }
+ }
+ matches
+ }
+
+ /// Query for all matching elements starting from the given location.
+ pub fn query_after(&self, selector: Selector, location: Location) -> Vec<Content> {
+ self.all()
+ .skip_while(|elem| elem.location() != Some(location))
+ .filter(|elem| selector.matches(elem))
+ .cloned()
+ .collect()
+ }
+
+ /// The total number pages.
+ pub fn pages(&self) -> NonZeroUsize {
+ NonZeroUsize::new(self.pages).unwrap_or(NonZeroUsize::ONE)
+ }
+
+ /// Find the page number for the given location.
+ pub fn page(&self, location: Location) -> NonZeroUsize {
+ self.position(location).page
+ }
+
+ /// Find the position for the given location.
+ pub fn position(&self, location: Location) -> Position {
+ self.elems
+ .iter()
+ .find(|(elem, _)| elem.location() == Some(location))
+ .map(|(_, loc)| *loc)
+ .unwrap_or(Position { page: NonZeroUsize::ONE, point: Point::zero() })
+ }
+}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 6015c365..7458dc3c 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -1,14 +1,87 @@
//! The document model.
-#[macro_use]
-mod styles;
mod content;
+mod element;
+mod introspect;
mod realize;
-mod typeset;
+mod styles;
pub use self::content::*;
+pub use self::element::*;
+pub use self::introspect::*;
pub use self::realize::*;
pub use self::styles::*;
-pub use self::typeset::*;
-pub use typst_macros::node;
+pub use typst_macros::element;
+
+use comemo::{Constraint, Track, Tracked, TrackedMut};
+
+use crate::diag::SourceResult;
+use crate::doc::Document;
+use crate::eval::Tracer;
+use crate::World;
+
+/// Typeset content into a fully layouted document.
+#[comemo::memoize]
+pub fn typeset(
+ world: Tracked<dyn World>,
+ mut tracer: TrackedMut<Tracer>,
+ content: &Content,
+) -> SourceResult<Document> {
+ let library = world.library();
+ let styles = StyleChain::new(&library.styles);
+
+ let mut document;
+ let mut iter = 0;
+ let mut introspector = Introspector::new(&[]);
+
+ // Relayout until all introspections stabilize.
+ // If that doesn't happen within five attempts, we give up.
+ loop {
+ let constraint = Constraint::new();
+ let mut provider = StabilityProvider::new();
+ let mut vt = Vt {
+ world,
+ tracer: TrackedMut::reborrow_mut(&mut tracer),
+ provider: provider.track_mut(),
+ introspector: introspector.track_with(&constraint),
+ };
+
+ document = (library.items.layout)(&mut vt, content, styles)?;
+ iter += 1;
+
+ introspector = Introspector::new(&document.pages);
+
+ if iter >= 5 || introspector.valid(&constraint) {
+ break;
+ }
+ }
+
+ Ok(document)
+}
+
+/// A virtual typesetter.
+///
+/// Holds the state needed to [typeset] content.
+pub struct Vt<'a> {
+ /// The compilation environment.
+ pub world: Tracked<'a, dyn World>,
+ /// The tracer for inspection of the values an expression produces.
+ pub tracer: TrackedMut<'a, Tracer>,
+ /// Provides stable identities to elements.
+ pub provider: TrackedMut<'a, StabilityProvider>,
+ /// Provides access to information about the document.
+ pub introspector: Tracked<'a, Introspector>,
+}
+
+impl Vt<'_> {
+ /// Mutably reborrow with a shorter lifetime.
+ pub fn reborrow_mut(&mut self) -> Vt<'_> {
+ Vt {
+ world: self.world,
+ tracer: TrackedMut::reborrow_mut(&mut self.tracer),
+ provider: TrackedMut::reborrow_mut(&mut self.provider),
+ introspector: self.introspector,
+ }
+ }
+}
diff --git a/src/model/realize.rs b/src/model/realize.rs
index 634a31fd..51d69fdc 100644
--- a/src/model/realize.rs
+++ b/src/model/realize.rs
@@ -1,4 +1,4 @@
-use super::{Content, MetaNode, Node, NodeId, Recipe, Selector, StyleChain, Vt};
+use super::{Content, ElemFunc, Element, MetaElem, Recipe, Selector, StyleChain, Vt};
use crate::diag::SourceResult;
use crate::doc::Meta;
use crate::util::hash128;
@@ -35,28 +35,28 @@ pub fn realize(
) -> SourceResult<Option<Content>> {
// Pre-process.
if target.needs_preparation() {
- let mut node = target.clone();
+ let mut elem = target.clone();
if target.can::<dyn Locatable>() || target.label().is_some() {
- let id = vt.provider.identify(hash128(target));
- node.set_stable_id(id);
+ let location = vt.provider.locate(hash128(target));
+ elem.set_location(location);
}
- if let Some(node) = node.with_mut::<dyn Synthesize>() {
- node.synthesize(vt, styles);
+ if let Some(elem) = elem.with_mut::<dyn Synthesize>() {
+ elem.synthesize(vt, styles);
}
- node.mark_prepared();
+ elem.mark_prepared();
- if node.stable_id().is_some() {
- let span = node.span();
- let meta = Meta::Node(node.clone());
+ if elem.location().is_some() {
+ let span = elem.span();
+ let meta = Meta::Elem(elem.clone());
return Ok(Some(
- (node + MetaNode::new().pack().spanned(span))
- .styled(MetaNode::set_data(vec![meta])),
+ (elem + MetaElem::new().pack().spanned(span))
+ .styled(MetaElem::set_data(vec![meta])),
));
}
- return Ok(Some(node));
+ return Ok(Some(elem));
}
// Find out how many recipes there are.
@@ -77,17 +77,17 @@ pub fn realize(
// Realize if there was no matching recipe.
if let Some(showable) = target.with::<dyn Show>() {
- let guard = Guard::Base(target.id());
+ let guard = Guard::Base(target.func());
if realized.is_none() && !target.is_guarded(guard) {
realized = Some(showable.show(vt, styles)?);
}
}
- // Finalize only if this is the first application for this node.
- if let Some(node) = target.with::<dyn Finalize>() {
+ // Finalize only if this is the first application for this element.
+ if let Some(elem) = target.with::<dyn Finalize>() {
if target.is_pristine() {
if let Some(already) = realized {
- realized = Some(node.finalize(already, styles));
+ realized = Some(elem.finalize(already, styles));
}
}
}
@@ -103,8 +103,8 @@ fn try_apply(
guard: Guard,
) -> SourceResult<Option<Content>> {
match &recipe.selector {
- Some(Selector::Node(id, _)) => {
- if target.id() != *id {
+ Some(Selector::Elem(element, _)) => {
+ if target.func() != *element {
return Ok(None);
}
@@ -124,22 +124,17 @@ fn try_apply(
return Ok(None);
};
- let make = |s| {
- let mut content = item!(text)(s);
- content.copy_modifiers(target);
- content
- };
-
+ let make = |s: &str| target.clone().with_field("text", s);
let mut result = vec![];
let mut cursor = 0;
for m in regex.find_iter(&text) {
let start = m.start();
if cursor < start {
- result.push(make(text[cursor..start].into()));
+ result.push(make(&text[cursor..start]));
}
- let piece = make(m.as_str().into()).guarded(guard);
+ let piece = make(m.as_str()).guarded(guard);
let transformed = recipe.apply_vt(vt, piece)?;
result.push(transformed);
cursor = m.end();
@@ -150,7 +145,7 @@ fn try_apply(
}
if cursor < text.len() {
- result.push(make(text[cursor..].into()));
+ result.push(make(&text[cursor..]));
}
Ok(Some(Content::sequence(result)))
@@ -163,55 +158,56 @@ fn try_apply(
}
}
-/// Makes this node locatable through `vt.locate`.
+/// Makes this element locatable through `vt.locate`.
pub trait Locatable {}
-/// Synthesize fields on a node. This happens before execution of any show rule.
+/// Synthesize fields on an element. This happens before execution of any show
+/// rule.
pub trait Synthesize {
- /// Prepare the node for show rule application.
+ /// Prepare the element for show rule application.
fn synthesize(&mut self, vt: &Vt, styles: StyleChain);
}
-/// The base recipe for a node.
+/// The base recipe for an element.
pub trait Show {
- /// Execute the base recipe for this node.
+ /// Execute the base recipe for this element.
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content>;
}
-/// Post-process a node after it was realized.
+/// Post-process an element after it was realized.
pub trait Finalize {
- /// Finalize the fully realized form of the node. Use this for effects that
+ /// Finalize the fully realized form of the element. Use this for effects that
/// should work even in the face of a user-defined show rule, for example
- /// the linking behaviour of a link node.
+ /// the linking behaviour of a link element.
fn finalize(&self, realized: Content, styles: StyleChain) -> Content;
}
-/// How a node interacts with other nodes.
+/// How the element interacts with other elements.
pub trait Behave {
- /// The node's interaction behaviour.
+ /// The element's interaction behaviour.
fn behaviour(&self) -> Behaviour;
- /// Whether this weak node is larger than a previous one and thus picked as
- /// the maximum when the levels are the same.
+ /// Whether this weak element is larger than a previous one and thus picked
+ /// as the maximum when the levels are the same.
#[allow(unused_variables)]
fn larger(&self, prev: &Content) -> bool {
false
}
}
-/// How a node interacts with other nodes in a stream.
+/// How an element interacts with other elements in a stream.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Behaviour {
- /// A weak node which only survives when a supportive node is before and
- /// after it. Furthermore, per consecutive run of weak nodes, only one
- /// survives: The one with the lowest weakness level (or the larger one if
- /// there is a tie).
+ /// A weak element which only survives when a supportive element is before
+ /// and after it. Furthermore, per consecutive run of weak elements, only
+ /// one survives: The one with the lowest weakness level (or the larger one
+ /// if there is a tie).
Weak(usize),
- /// A node that enables adjacent weak nodes to exist. The default.
+ /// An element that enables adjacent weak elements to exist. The default.
Supportive,
- /// A node that destroys adjacent weak nodes.
+ /// An element that destroys adjacent weak elements.
Destructive,
- /// A node that does not interact at all with other nodes, having the
+ /// An element that does not interact at all with other elements, having the
/// same effect as if it didn't exist.
Ignorant,
}
@@ -221,6 +217,6 @@ pub enum Behaviour {
pub enum Guard {
/// The nth recipe from the top of the chain.
Nth(usize),
- /// The [base recipe](Show) for a kind of node.
- Base(NodeId),
+ /// The [base recipe](Show) for a kind of element.
+ Base(ElemFunc),
}
diff --git a/src/model/styles.rs b/src/model/styles.rs
index b7d09774..db2b2053 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,25 +1,26 @@
use std::fmt::{self, Debug, Formatter, Write};
use std::iter;
+use std::mem;
-use ecow::{eco_format, EcoString, EcoVec};
+use ecow::{eco_format, eco_vec, EcoString, EcoVec};
-use super::{Content, Label, Node, NodeId, Vt};
+use super::{Content, ElemFunc, Element, Label, Vt};
use crate::diag::{SourceResult, Trace, Tracepoint};
use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value, Vm};
use crate::syntax::Span;
use crate::util::pretty_array_like;
-/// A map of style properties.
-#[derive(Default, Clone, Hash)]
-pub struct StyleMap(Vec<Style>);
+/// A list of style properties.
+#[derive(Default, PartialEq, Clone, Hash)]
+pub struct Styles(EcoVec<Style>);
-impl StyleMap {
- /// Create a new, empty style map.
+impl Styles {
+ /// Create a new, empty style list.
pub fn new() -> Self {
Self::default()
}
- /// Whether this map contains no styles.
+ /// Whether this contains no styles.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
@@ -39,13 +40,25 @@ impl StyleMap {
}
/// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place.
- pub fn apply(&mut self, outer: Self) {
- self.0.splice(0..0, outer.0.iter().cloned());
+ pub fn apply(&mut self, mut outer: Self) {
+ outer.0.extend(mem::take(self).0.into_iter());
+ *self = outer;
+ }
+
+ /// Apply one outer styles. Like [`chain_one`](StyleChain::chain_one), but
+ /// in-place.
+ pub fn apply_one(&mut self, outer: Style) {
+ self.0.insert(0, outer);
+ }
+
+ /// Apply a slice of outer styles.
+ pub fn apply_slice(&mut self, outer: &[Style]) {
+ self.0 = outer.iter().cloned().chain(mem::take(self).0.into_iter()).collect();
}
/// Add an origin span to all contained properties.
pub fn spanned(mut self, span: Span) -> Self {
- for entry in &mut self.0 {
+ for entry in self.0.make_mut() {
if let Style::Property(property) = entry {
property.span = Some(span);
}
@@ -53,37 +66,31 @@ impl StyleMap {
self
}
- /// Returns `Some(_)` with an optional span if this map contains styles for
- /// the given `node`.
- pub fn interruption<T: Node>(&self) -> Option<Option<Span>> {
- let node = NodeId::of::<T>();
+ /// Returns `Some(_)` with an optional span if this list contains
+ /// styles for the given element.
+ pub fn interruption<T: Element>(&self) -> Option<Option<Span>> {
+ let func = T::func();
self.0.iter().find_map(|entry| match entry {
- Style::Property(property) => property.is_of(node).then(|| property.span),
- Style::Recipe(recipe) => recipe.is_of(node).then(|| Some(recipe.span)),
+ Style::Property(property) => property.is_of(func).then(|| property.span),
+ Style::Recipe(recipe) => recipe.is_of(func).then(|| Some(recipe.span)),
})
}
}
-impl From<Style> for StyleMap {
+impl From<Style> for Styles {
fn from(entry: Style) -> Self {
- Self(vec![entry])
- }
-}
-
-impl PartialEq for StyleMap {
- fn eq(&self, other: &Self) -> bool {
- crate::util::hash128(self) == crate::util::hash128(other)
+ Self(eco_vec![entry])
}
}
-impl Debug for StyleMap {
+impl Debug for Styles {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("..")
}
}
/// A single style property or recipe.
-#[derive(Clone, Hash)]
+#[derive(Clone, PartialEq, Hash)]
pub enum Style {
/// A style property originating from a set rule or constructor.
Property(Property),
@@ -124,11 +131,17 @@ impl From<Property> for Style {
}
}
+impl From<Recipe> for Style {
+ fn from(recipe: Recipe) -> Self {
+ Self::Recipe(recipe)
+ }
+}
+
/// A style property originating from a set rule or constructor.
-#[derive(Clone, Hash)]
+#[derive(Clone, PartialEq, Hash)]
pub struct Property {
- /// The id of the node the property belongs to.
- node: NodeId,
+ /// The element the property belongs to.
+ element: ElemFunc,
/// The property's name.
name: EcoString,
/// The property's value.
@@ -139,44 +152,44 @@ pub struct Property {
impl Property {
/// Create a new property from a key-value pair.
- pub fn new(node: NodeId, name: EcoString, value: Value) -> Self {
- Self { node, name, value, span: None }
+ pub fn new(element: ElemFunc, name: EcoString, value: Value) -> Self {
+ Self { element, name, value, span: None }
}
/// Whether this property is the given one.
- pub fn is(&self, node: NodeId, name: &str) -> bool {
- self.node == node && self.name == name
+ pub fn is(&self, element: ElemFunc, name: &str) -> bool {
+ self.element == element && self.name == name
}
- /// Whether this property belongs to the node with the given id.
- pub fn is_of(&self, node: NodeId) -> bool {
- self.node == node
+ /// Whether this property belongs to the given element.
+ pub fn is_of(&self, element: ElemFunc) -> bool {
+ self.element == element
}
}
impl Debug for Property {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "set {}({}: {:?})", self.node.name, self.name, self.value)?;
+ write!(f, "set {}({}: {:?})", self.element.name(), self.name, self.value)?;
Ok(())
}
}
/// A show rule recipe.
-#[derive(Clone, Hash)]
+#[derive(Clone, PartialEq, Hash)]
pub struct Recipe {
/// The span errors are reported with.
pub span: Span,
- /// Determines whether the recipe applies to a node.
+ /// Determines whether the recipe applies to an element.
pub selector: Option<Selector>,
/// The transformation to perform on the match.
pub transform: Transform,
}
impl Recipe {
- /// Whether this recipe is for the given node.
- pub fn is_of(&self, node: NodeId) -> bool {
+ /// Whether this recipe is for the given type of element.
+ pub fn is_of(&self, element: ElemFunc) -> bool {
match self.selector {
- Some(Selector::Node(id, _)) => id == node,
+ Some(Selector::Elem(own, _)) => own == element,
_ => false,
}
}
@@ -197,7 +210,7 @@ impl Recipe {
let mut result = func.call_vm(vm, args);
// For selector-less show rules, a tracepoint makes no sense.
if self.selector.is_some() {
- let point = || Tracepoint::Show(content.id().name.into());
+ let point = || Tracepoint::Show(content.func().name().into());
result = result.trace(vm.world(), point, content.span());
}
Ok(result?.display())
@@ -213,7 +226,7 @@ impl Recipe {
Transform::Func(func) => {
let mut result = func.call_vt(vt, [Value::Content(content.clone())]);
if self.selector.is_some() {
- let point = || Tracepoint::Show(content.id().name.into());
+ let point = || Tracepoint::Show(content.func().name().into());
result = result.trace(vt.world, point, content.span());
}
Ok(result?.display())
@@ -238,25 +251,20 @@ impl Debug for Recipe {
/// A selector in a show rule.
#[derive(Clone, PartialEq, Hash)]
pub enum Selector {
- /// Matches a specific type of node.
+ /// Matches a specific type of element.
///
- /// If there is a dictionary, only nodes with the fields from the
+ /// If there is a dictionary, only elements with the fields from the
/// dictionary match.
- Node(NodeId, Option<Dict>),
- /// Matches nodes with a specific label.
+ Elem(ElemFunc, Option<Dict>),
+ /// Matches elements with a specific label.
Label(Label),
- /// Matches text nodes through a regular expression.
+ /// Matches text elements through a regular expression.
Regex(Regex),
/// Matches if any of the subselectors match.
Any(EcoVec<Self>),
}
impl Selector {
- /// Define a simple node selector.
- pub fn node<T: Node>() -> Self {
- Self::Node(NodeId::of::<T>(), None)
- }
-
/// Define a simple text selector.
pub fn text(text: &str) -> Self {
Self::Regex(Regex::new(&regex::escape(text)).unwrap())
@@ -265,16 +273,16 @@ impl Selector {
/// Whether the selector matches for the target.
pub fn matches(&self, target: &Content) -> bool {
match self {
- Self::Node(id, dict) => {
- target.id() == *id
+ Self::Elem(element, dict) => {
+ target.func() == *element
&& dict
.iter()
.flat_map(|dict| dict.iter())
- .all(|(name, value)| target.field(name) == Some(value))
+ .all(|(name, value)| target.field_ref(name) == Some(value))
}
Self::Label(label) => target.label() == Some(label),
Self::Regex(regex) => {
- target.id() == item!(text_id)
+ target.func() == item!(text_func)
&& item!(text_str)(target).map_or(false, |text| regex.is_match(&text))
}
Self::Any(selectors) => selectors.iter().any(|sel| sel.matches(target)),
@@ -285,8 +293,8 @@ impl Selector {
impl Debug for Selector {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
- Self::Node(node, dict) => {
- f.write_str(node.name)?;
+ Self::Elem(elem, dict) => {
+ f.write_str(elem.name())?;
if let Some(dict) = dict {
f.write_str(".where")?;
dict.fmt(f)?;
@@ -307,21 +315,24 @@ impl Debug for Selector {
cast_from_value! {
Selector: "selector",
- text: EcoString => Self::text(&text),
+ func: Func => func
+ .element()
+ .ok_or("only element functions can be used as selectors")?
+ .select(),
label: Label => Self::Label(label),
- func: Func => func.select(None)?,
+ text: EcoString => Self::text(&text),
regex: Regex => Self::Regex(regex),
}
/// A show rule transformation that can be applied to a match.
-#[derive(Clone, Hash)]
+#[derive(Clone, PartialEq, Hash)]
pub enum Transform {
/// Replacement content.
Content(Content),
/// A function to apply to the match.
Func(Func),
/// Apply styles to the content.
- Style(StyleMap),
+ Style(Styles),
}
impl Debug for Transform {
@@ -340,11 +351,11 @@ cast_from_value! {
func: Func => Self::Func(func),
}
-/// A chain of style maps, similar to a linked list.
+/// A chain of styles, similar to a linked list.
///
-/// A style chain allows to combine properties from multiple style maps in a
-/// node hierarchy in a non-allocating way. Rather than eagerly merging the
-/// maps, each access walks the hierarchy from the innermost to the outermost
+/// A style chain allows to combine properties from multiple style lists in a
+/// element hierarchy in a non-allocating way. Rather than eagerly merging the
+/// lists, each access walks the hierarchy from the innermost to the outermost
/// map, trying to find a match and then folding it with matches further up the
/// chain.
#[derive(Default, Clone, Copy, Hash)]
@@ -356,21 +367,21 @@ pub struct StyleChain<'a> {
}
impl<'a> StyleChain<'a> {
- /// Start a new style chain with a root map.
- pub fn new(root: &'a StyleMap) -> Self {
+ /// Start a new style chain with root styles.
+ pub fn new(root: &'a Styles) -> Self {
Self { head: &root.0, tail: None }
}
- /// Make the given map the first link of this chain.
+ /// Make the given style list the first link of this chain.
///
- /// The resulting style chain contains styles from `map` as well as
- /// `self`. The ones from `map` take precedence over the ones from
- /// `self`. For folded properties `map` contributes the inner value.
- pub fn chain<'b>(&'b self, map: &'b StyleMap) -> StyleChain<'b> {
- if map.is_empty() {
+ /// The resulting style chain contains styles from `local` as well as
+ /// `self`. The ones from `local` take precedence over the ones from
+ /// `self`. For folded properties `local` contributes the inner value.
+ pub fn chain<'b>(&'b self, local: &'b Styles) -> StyleChain<'b> {
+ if local.is_empty() {
*self
} else {
- StyleChain { head: &map.0, tail: Some(self) }
+ StyleChain { head: &local.0, tail: Some(self) }
}
}
@@ -385,12 +396,12 @@ impl<'a> StyleChain<'a> {
/// Cast the first value for the given property in the chain.
pub fn get<T: Cast>(
self,
- node: NodeId,
+ func: ElemFunc,
name: &'a str,
inherent: Option<Value>,
default: impl Fn() -> T,
) -> T {
- self.properties::<T>(node, name, inherent)
+ self.properties::<T>(func, name, inherent)
.next()
.unwrap_or_else(default)
}
@@ -398,18 +409,18 @@ impl<'a> StyleChain<'a> {
/// Cast the first value for the given property in the chain.
pub fn get_resolve<T: Cast + Resolve>(
self,
- node: NodeId,
+ func: ElemFunc,
name: &'a str,
inherent: Option<Value>,
default: impl Fn() -> T,
) -> T::Output {
- self.get(node, name, inherent, default).resolve(self)
+ self.get(func, name, inherent, default).resolve(self)
}
/// Cast the first value for the given property in the chain.
pub fn get_fold<T: Cast + Fold>(
self,
- node: NodeId,
+ func: ElemFunc,
name: &'a str,
inherent: Option<Value>,
default: impl Fn() -> T::Output,
@@ -424,13 +435,13 @@ impl<'a> StyleChain<'a> {
.map(|value| value.fold(next(values, styles, default)))
.unwrap_or_else(|| default())
}
- next(self.properties::<T>(node, name, inherent), self, &default)
+ next(self.properties::<T>(func, name, inherent), self, &default)
}
/// Cast the first value for the given property in the chain.
pub fn get_resolve_fold<T>(
self,
- node: NodeId,
+ func: ElemFunc,
name: &'a str,
inherent: Option<Value>,
default: impl Fn() -> <T::Output as Fold>::Output,
@@ -453,7 +464,7 @@ impl<'a> StyleChain<'a> {
.map(|value| value.resolve(styles).fold(next(values, styles, default)))
.unwrap_or_else(|| default())
}
- next(self.properties::<T>(node, name, inherent), self, &default)
+ next(self.properties::<T>(func, name, inherent), self, &default)
}
/// Iterate over all style recipes in the chain.
@@ -464,7 +475,7 @@ impl<'a> StyleChain<'a> {
/// Iterate over all values for the given property in the chain.
pub fn properties<T: Cast + 'a>(
self,
- node: NodeId,
+ func: ElemFunc,
name: &'a str,
inherent: Option<Value>,
) -> impl Iterator<Item = T> + '_ {
@@ -473,21 +484,21 @@ impl<'a> StyleChain<'a> {
.chain(
self.entries()
.filter_map(Style::property)
- .filter(move |property| property.is(node, name))
+ .filter(move |property| property.is(func, name))
.map(|property| property.value.clone()),
)
.map(move |value| {
- value
- .cast()
- .unwrap_or_else(|err| panic!("{} (for {}.{})", err, node.name, name))
+ value.cast().unwrap_or_else(|err| {
+ panic!("{} (for {}.{})", err, func.name(), name)
+ })
})
}
/// Convert to a style map.
- pub fn to_map(self) -> StyleMap {
- let mut suffix = StyleMap::new();
+ pub fn to_map(self) -> Styles {
+ let mut suffix = Styles::new();
for link in self.links() {
- suffix.0.splice(0..0, link.iter().cloned());
+ suffix.apply_slice(link);
}
suffix
}
@@ -502,13 +513,13 @@ impl<'a> StyleChain<'a> {
Links(Some(self))
}
- /// Build a style map from the suffix (all links beyond the `len`) of the
+ /// Build owned styles from the suffix (all links beyond the `len`) of the
/// chain.
- fn suffix(self, len: usize) -> StyleMap {
- let mut suffix = StyleMap::new();
+ fn suffix(self, len: usize) -> Styles {
+ let mut suffix = Styles::new();
let take = self.links().count().saturating_sub(len);
for link in self.links().take(take) {
- suffix.0.splice(0..0, link.iter().cloned());
+ suffix.apply_slice(link);
}
suffix
}
@@ -517,6 +528,16 @@ impl<'a> StyleChain<'a> {
fn pop(&mut self) {
*self = self.tail.copied().unwrap_or_default();
}
+
+ /// Whether two style chains contain the same pointers.
+ fn ptr_eq(self, other: Self) -> bool {
+ std::ptr::eq(self.head, other.head)
+ && match (self.tail, other.tail) {
+ (Some(a), Some(b)) => std::ptr::eq(a, b),
+ (None, None) => true,
+ _ => false,
+ }
+ }
}
impl Debug for StyleChain<'_> {
@@ -530,7 +551,7 @@ impl Debug for StyleChain<'_> {
impl PartialEq for StyleChain<'_> {
fn eq(&self, other: &Self) -> bool {
- crate::util::hash128(self) == crate::util::hash128(other)
+ self.ptr_eq(*other) || crate::util::hash128(self) == crate::util::hash128(other)
}
}
@@ -574,7 +595,7 @@ impl<'a> Iterator for Links<'a> {
#[derive(Clone, Hash)]
pub struct StyleVec<T> {
items: Vec<T>,
- maps: Vec<(StyleMap, usize)>,
+ styles: Vec<(Styles, usize)>,
}
impl<T> StyleVec<T> {
@@ -588,14 +609,14 @@ impl<T> StyleVec<T> {
self.items.len()
}
- /// Insert an element in the front. The element will share the style of the
- /// current first element.
+ /// Insert an item in the front. The item will share the style of the
+ /// current first item.
///
/// This method has no effect if the vector is empty.
pub fn push_front(&mut self, item: T) {
- if !self.maps.is_empty() {
+ if !self.styles.is_empty() {
self.items.insert(0, item);
- self.maps[0].1 += 1;
+ self.styles[0].1 += 1;
}
}
@@ -606,14 +627,14 @@ impl<T> StyleVec<T> {
{
StyleVec {
items: self.items.iter().map(f).collect(),
- maps: self.maps.clone(),
+ styles: self.styles.clone(),
}
}
- /// Iterate over references to the contained items and associated style maps.
- pub fn iter(&self) -> impl Iterator<Item = (&T, &StyleMap)> + '_ {
+ /// Iterate over references to the contained items and associated styles.
+ pub fn iter(&self) -> impl Iterator<Item = (&T, &Styles)> + '_ {
self.items().zip(
- self.maps
+ self.styles
.iter()
.flat_map(|(map, count)| iter::repeat(map).take(*count)),
)
@@ -624,13 +645,13 @@ impl<T> StyleVec<T> {
self.items.iter()
}
- /// Iterate over the contained maps. Note that zipping this with `items()`
- /// does not yield the same result as calling `iter()` because this method
- /// only returns maps once that are shared by consecutive items. This method
- /// is designed for use cases where you want to check, for example, whether
- /// any of the maps fulfills a specific property.
- pub fn styles(&self) -> impl Iterator<Item = &StyleMap> {
- self.maps.iter().map(|(map, _)| map)
+ /// Iterate over the contained style lists. Note that zipping this with
+ /// `items()` does not yield the same result as calling `iter()` because
+ /// this method only returns lists once that are shared by consecutive
+ /// items. This method is designed for use cases where you want to check,
+ /// for example, whether any of the lists fulfills a specific property.
+ pub fn styles(&self) -> impl Iterator<Item = &Styles> {
+ self.styles.iter().map(|(map, _)| map)
}
}
@@ -639,35 +660,35 @@ impl StyleVec<Content> {
self.items
.into_iter()
.zip(
- self.maps
+ self.styles
.iter()
.flat_map(|(map, count)| iter::repeat(map).take(*count)),
)
- .map(|(content, map)| content.styled_with_map(map.clone()))
+ .map(|(content, styles)| content.styled_with_map(styles.clone()))
.collect()
}
}
impl<T> Default for StyleVec<T> {
fn default() -> Self {
- Self { items: vec![], maps: vec![] }
+ Self { items: vec![], styles: vec![] }
}
}
impl<T> FromIterator<T> for StyleVec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let items: Vec<_> = iter.into_iter().collect();
- let maps = vec![(StyleMap::new(), items.len())];
- Self { items, maps }
+ let styles = vec![(Styles::new(), items.len())];
+ Self { items, styles }
}
}
impl<T: Debug> Debug for StyleVec<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_list()
- .entries(self.iter().map(|(item, map)| {
+ .entries(self.iter().map(|(item, styles)| {
crate::util::debug(|f| {
- map.fmt(f)?;
+ styles.fmt(f)?;
item.fmt(f)
})
}))
@@ -708,7 +729,7 @@ impl<'a, T> StyleVecBuilder<'a, T> {
}
/// Iterate over the contained items.
- pub fn items(&self) -> std::slice::Iter<'_, T> {
+ pub fn elems(&self) -> std::slice::Iter<'_, T> {
self.items.iter()
}
@@ -743,13 +764,13 @@ impl<'a, T> StyleVecBuilder<'a, T> {
}
}
- let maps = self
+ let styles = self
.chains
.into_iter()
.map(|(chain, count)| (chain.suffix(shared), count))
.collect();
- (StyleVec { items: self.items, maps }, trunk)
+ (StyleVec { items: self.items, styles }, trunk)
}
}
diff --git a/src/model/typeset.rs b/src/model/typeset.rs
deleted file mode 100644
index 8216d7a8..00000000
--- a/src/model/typeset.rs
+++ /dev/null
@@ -1,241 +0,0 @@
-use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
-use std::num::NonZeroUsize;
-
-use comemo::{Constraint, Track, Tracked, TrackedMut};
-
-use super::{Content, Selector, StyleChain};
-use crate::diag::SourceResult;
-use crate::doc::{Document, Element, Frame, Location, Meta};
-use crate::eval::{cast_from_value, Tracer};
-use crate::geom::{Point, Transform};
-use crate::util::NonZeroExt;
-use crate::World;
-
-/// Typeset content into a fully layouted document.
-#[comemo::memoize]
-pub fn typeset(
- world: Tracked<dyn World>,
- mut tracer: TrackedMut<Tracer>,
- content: &Content,
-) -> SourceResult<Document> {
- let library = world.library();
- let styles = StyleChain::new(&library.styles);
-
- let mut document;
- let mut iter = 0;
- let mut introspector = Introspector::new(&[]);
-
- // Relayout until all introspections stabilize.
- // If that doesn't happen within five attempts, we give up.
- loop {
- let constraint = Constraint::new();
- let mut provider = StabilityProvider::new();
- let mut vt = Vt {
- world,
- tracer: TrackedMut::reborrow_mut(&mut tracer),
- provider: provider.track_mut(),
- introspector: introspector.track_with(&constraint),
- };
-
- document = (library.items.layout)(&mut vt, content, styles)?;
- iter += 1;
-
- introspector = Introspector::new(&document.pages);
- introspector.init = true;
-
- if iter >= 5 || introspector.valid(&constraint) {
- break;
- }
- }
-
- Ok(document)
-}
-
-/// A virtual typesetter.
-///
-/// Holds the state needed to [typeset] content.
-pub struct Vt<'a> {
- /// The compilation environment.
- pub world: Tracked<'a, dyn World>,
- /// The tracer for inspection of the values an expression produces.
- pub tracer: TrackedMut<'a, Tracer>,
- /// Provides stable identities to nodes.
- pub provider: TrackedMut<'a, StabilityProvider>,
- /// Provides access to information about the document.
- pub introspector: Tracked<'a, Introspector>,
-}
-
-impl Vt<'_> {
- /// Mutably reborrow with a shorter lifetime.
- pub fn reborrow_mut(&mut self) -> Vt<'_> {
- Vt {
- world: self.world,
- tracer: TrackedMut::reborrow_mut(&mut self.tracer),
- provider: TrackedMut::reborrow_mut(&mut self.provider),
- introspector: self.introspector,
- }
- }
-}
-
-/// Provides stable identities to nodes.
-#[derive(Clone)]
-pub struct StabilityProvider {
- hashes: Vec<u128>,
- checkpoints: Vec<usize>,
-}
-
-impl StabilityProvider {
- /// Create a new stability provider.
- pub fn new() -> Self {
- Self { hashes: vec![], checkpoints: vec![] }
- }
-}
-
-#[comemo::track]
-impl StabilityProvider {
- /// Produce a stable identifier for this call site.
- pub fn identify(&mut self, hash: u128) -> StableId {
- let count = self.hashes.iter().filter(|&&prev| prev == hash).count();
- self.hashes.push(hash);
- StableId(hash, count, 0)
- }
-
- /// Create a checkpoint of the state that can be restored.
- pub fn save(&mut self) {
- self.checkpoints.push(self.hashes.len());
- }
-
- /// Restore the last checkpoint.
- pub fn restore(&mut self) {
- if let Some(checkpoint) = self.checkpoints.pop() {
- self.hashes.truncate(checkpoint);
- }
- }
-}
-
-/// Stably identifies a call site across multiple layout passes.
-///
-/// This struct is created by [`StabilityProvider::identify`].
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct StableId(u128, usize, usize);
-
-impl StableId {
- /// Produce a variant of this id.
- pub fn variant(self, n: usize) -> Self {
- Self(self.0, self.1, n)
- }
-}
-
-impl Debug for StableId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad("..")
- }
-}
-
-cast_from_value! {
- StableId: "stable id",
-}
-
-/// Provides access to information about the document.
-pub struct Introspector {
- init: bool,
- pages: usize,
- nodes: Vec<(Content, Location)>,
-}
-
-impl Introspector {
- /// Create a new introspector.
- pub fn new(frames: &[Frame]) -> Self {
- let mut introspector = Self { init: false, pages: frames.len(), nodes: vec![] };
- for (i, frame) in frames.iter().enumerate() {
- let page = NonZeroUsize::new(1 + i).unwrap();
- introspector.extract(frame, page, Transform::identity());
- }
- introspector
- }
-
- /// Iterate over all nodes.
- pub fn all(&self) -> impl Iterator<Item = &Content> {
- self.nodes.iter().map(|(node, _)| node)
- }
-
- /// Extract metadata from a frame.
- fn extract(&mut self, frame: &Frame, page: NonZeroUsize, ts: Transform) {
- for (pos, element) in frame.elements() {
- match element {
- Element::Group(group) => {
- let ts = ts
- .pre_concat(Transform::translate(pos.x, pos.y))
- .pre_concat(group.transform);
- self.extract(&group.frame, page, ts);
- }
- Element::Meta(Meta::Node(content), _)
- if !self
- .nodes
- .iter()
- .any(|(prev, _)| prev.stable_id() == content.stable_id()) =>
- {
- let pos = pos.transform(ts);
- self.nodes.push((content.clone(), Location { page, pos }));
- }
- _ => {}
- }
- }
- }
-}
-
-#[comemo::track]
-impl Introspector {
- /// Whether this introspector is not yet initialized.
- pub fn init(&self) -> bool {
- self.init
- }
-
- /// Query for all nodes for the given selector.
- pub fn query(&self, selector: Selector) -> Vec<Content> {
- self.all().filter(|node| selector.matches(node)).cloned().collect()
- }
-
- /// Query for all nodes up to the given id.
- pub fn query_before(&self, selector: Selector, id: StableId) -> Vec<Content> {
- let mut matches = vec![];
- for node in self.all() {
- if selector.matches(node) {
- matches.push(node.clone());
- }
- if node.stable_id() == Some(id) {
- break;
- }
- }
- matches
- }
-
- /// Query for all nodes starting from the given id.
- pub fn query_after(&self, selector: Selector, id: StableId) -> Vec<Content> {
- self.all()
- .skip_while(|node| node.stable_id() != Some(id))
- .filter(|node| selector.matches(node))
- .cloned()
- .collect()
- }
-
- /// The total number pages.
- pub fn pages(&self) -> NonZeroUsize {
- NonZeroUsize::new(self.pages).unwrap_or(NonZeroUsize::ONE)
- }
-
- /// Find the page number for the given stable id.
- pub fn page(&self, id: StableId) -> NonZeroUsize {
- self.location(id).page
- }
-
- /// Find the location for the given stable id.
- pub fn location(&self, id: StableId) -> Location {
- self.nodes
- .iter()
- .find(|(node, _)| node.stable_id() == Some(id))
- .map(|(_, loc)| *loc)
- .unwrap_or(Location { page: NonZeroUsize::ONE, pos: Point::zero() })
- }
-}
diff --git a/src/syntax/kind.rs b/src/syntax/kind.rs
index 869d77af..c96539d1 100644
--- a/src/syntax/kind.rs
+++ b/src/syntax/kind.rs
@@ -4,11 +4,7 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum SyntaxKind {
- /// Markup of which all lines must have a minimal indentation.
- ///
- /// Notably, the number does not determine in which column the markup
- /// 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.
Markup,
/// Plain text without markup.
Text,
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
index 8e27d98d..3fea3fe1 100644
--- a/src/syntax/lexer.rs
+++ b/src/syntax/lexer.rs
@@ -473,7 +473,7 @@ impl Lexer<'_> {
c if is_id_start(c) => self.ident(start),
- _ => self.error("not valid here"),
+ _ => self.error("this character is not valid in code"),
}
}
@@ -634,7 +634,7 @@ fn count_newlines(text: &str) -> usize {
newlines
}
-/// Whether a string is a valid unicode identifier.
+/// Whether a string is a valid Typst identifier.
///
/// In addition to what is specified in the [Unicode Standard][uax31], we allow:
/// - `_` as a starting character,
@@ -651,13 +651,13 @@ pub fn is_ident(string: &str) -> bool {
/// Whether a character can start an identifier.
#[inline]
-pub fn is_id_start(c: char) -> bool {
+pub(crate) fn is_id_start(c: char) -> bool {
c.is_xid_start() || c == '_'
}
/// Whether a character can continue an identifier.
#[inline]
-pub fn is_id_continue(c: char) -> bool {
+pub(crate) fn is_id_continue(c: char) -> bool {
c.is_xid_continue() || c == '_' || c == '-'
}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index ae12e818..c27547c4 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -14,6 +14,5 @@ pub use self::kind::*;
pub use self::lexer::*;
pub use self::node::*;
pub use self::parser::*;
-pub use self::reparser::*;
pub use self::source::*;
pub use self::span::*;
diff --git a/src/syntax/source.rs b/src/syntax/source.rs
index 607a2603..052e841a 100644
--- a/src/syntax/source.rs
+++ b/src/syntax/source.rs
@@ -9,7 +9,8 @@ use comemo::Prehashed;
use unscanny::Scanner;
use super::ast::Markup;
-use super::{is_newline, parse, reparse, LinkedNode, Span, SyntaxNode};
+use super::reparser::reparse;
+use super::{is_newline, parse, LinkedNode, Span, SyntaxNode};
use crate::diag::SourceResult;
use crate::util::{PathExt, StrExt};
diff --git a/src/util/mod.rs b/src/util/mod.rs
index 596282de..3e0e7aa2 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -40,7 +40,7 @@ pub fn hash128<T: Hash + ?Sized>(value: &T) -> u128 {
state.finish128().as_u128()
}
-/// Extra methods for [`NonZeroUsize`].
+/// An extra constant for [`NonZeroUsize`].
pub trait NonZeroExt {
/// The number `1`.
const ONE: Self;
@@ -210,7 +210,13 @@ pub fn pretty_array_like(parts: &[impl AsRef<str>], trailing_comma: bool) -> Str
buf.push('(');
if list.contains('\n') {
buf.push('\n');
- buf.push_str(&indent(&list, 2));
+ for (i, line) in list.lines().enumerate() {
+ if i > 0 {
+ buf.push('\n');
+ }
+ buf.push_str(" ");
+ buf.push_str(line);
+ }
buf.push('\n');
} else {
buf.push_str(&list);
@@ -218,18 +224,3 @@ pub fn pretty_array_like(parts: &[impl AsRef<str>], trailing_comma: bool) -> Str
buf.push(')');
buf
}
-
-/// Indent a string by two spaces.
-pub fn indent(text: &str, amount: usize) -> String {
- let mut buf = String::new();
- for (i, line) in text.lines().enumerate() {
- if i > 0 {
- buf.push('\n');
- }
- for _ in 0..amount {
- buf.push(' ');
- }
- buf.push_str(line);
- }
- buf
-}
diff --git a/tests/ref/compiler/show-node.png b/tests/ref/compiler/show-node.png
index 014c45bf..cae9b932 100644
--- a/tests/ref/compiler/show-node.png
+++ b/tests/ref/compiler/show-node.png
Binary files differ
diff --git a/tests/ref/layout/place.png b/tests/ref/layout/place.png
index 10686ab5..968e613c 100644
--- a/tests/ref/layout/place.png
+++ b/tests/ref/layout/place.png
Binary files differ
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index 9e6d94a3..07312c5c 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -12,15 +12,15 @@ use elsa::FrozenVec;
use once_cell::unsync::OnceCell;
use tiny_skia as sk;
use typst::diag::{bail, FileError, FileResult};
-use typst::doc::{Document, Element, Frame, Meta};
+use typst::doc::{Document, Frame, FrameItem, Meta};
use typst::eval::{func, Library, Value};
use typst::font::{Font, FontBook};
use typst::geom::{Abs, Color, RgbaColor, Sides, Smart};
use typst::syntax::{Source, SourceId, Span, SyntaxNode};
use typst::util::{Buffer, PathExt};
use typst::World;
-use typst_library::layout::PageNode;
-use typst_library::text::{TextNode, TextSize};
+use typst_library::layout::PageElem;
+use typst_library::text::{TextElem, TextSize};
use unscanny::Scanner;
use walkdir::WalkDir;
@@ -179,12 +179,12 @@ fn library() -> Library {
// exactly 100pt wide. Page height is unbounded and font size is 10pt so
// that it multiplies to nice round numbers.
lib.styles
- .set(PageNode::set_width(Smart::Custom(Abs::pt(120.0).into())));
- lib.styles.set(PageNode::set_height(Smart::Auto));
- lib.styles.set(PageNode::set_margin(Sides::splat(Some(Smart::Custom(
+ .set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into())));
+ lib.styles.set(PageElem::set_height(Smart::Auto));
+ lib.styles.set(PageElem::set_margin(Sides::splat(Some(Smart::Custom(
Abs::pt(10.0).into(),
)))));
- lib.styles.set(TextNode::set_size(TextSize(Abs::pt(10.0).into())));
+ lib.styles.set(TextElem::set_size(TextSize(Abs::pt(10.0).into())));
// Hook up helpers into the global scope.
lib.global.scope_mut().define("test", test);
@@ -732,14 +732,14 @@ fn render(frames: &[Frame]) -> sk::Pixmap {
/// Draw extra boxes for links so we can see whether they are there.
fn render_links(canvas: &mut sk::Pixmap, ts: sk::Transform, frame: &Frame) {
- for (pos, element) in frame.elements() {
+ for (pos, item) in frame.items() {
let ts = ts.pre_translate(pos.x.to_pt() as f32, pos.y.to_pt() as f32);
- match *element {
- Element::Group(ref group) => {
+ match *item {
+ FrameItem::Group(ref group) => {
let ts = ts.pre_concat(group.transform.into());
render_links(canvas, ts, &group.frame);
}
- Element::Meta(Meta::Link(_), size) => {
+ FrameItem::Meta(Meta::Link(_), size) => {
let w = size.x.to_pt() as f32;
let h = size.y.to_pt() as f32;
let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap();
diff --git a/tests/typ/compiler/construct.typ b/tests/typ/compiler/construct.typ
index 53fcfefb..da077e7e 100644
--- a/tests/typ/compiler/construct.typ
+++ b/tests/typ/compiler/construct.typ
@@ -1,4 +1,4 @@
-// Test node functions.
+// Test constructors.
---
// Ensure that constructor styles aren't passed down the tree.
diff --git a/tests/typ/compiler/content-field.typ b/tests/typ/compiler/content-field.typ
index b67c090d..031a65b8 100644
--- a/tests/typ/compiler/content-field.typ
+++ b/tests/typ/compiler/content-field.typ
@@ -2,10 +2,10 @@
#let compute(equation, ..vars) = {
let vars = vars.named()
- let f(node) = {
- let func = node.func()
+ let f(elem) = {
+ let func = elem.func()
if func == text {
- let text = node.text
+ let text = elem.text
if regex("^\d+$") in text {
int(text)
} else if text in vars {
@@ -14,13 +14,13 @@
panic("unknown math variable: " + text)
}
} else if func == math.attach {
- let value = f(node.base)
- if node.has("top") {
- value = calc.pow(value, f(node.top))
+ let value = f(elem.base)
+ if elem.has("top") {
+ value = calc.pow(value, f(elem.top))
}
value
- } else if node.has("children") {
- node
+ } else if elem.has("children") {
+ elem
.children
.filter(v => v != [ ])
.split[+]
diff --git a/tests/typ/compiler/field.typ b/tests/typ/compiler/field.typ
index 49ffca04..7529cd85 100644
--- a/tests/typ/compiler/field.typ
+++ b/tests/typ/compiler/field.typ
@@ -13,9 +13,9 @@
}
---
-// Test field on node.
-#show list: node => {
- test(node.children.len(), 3)
+// Test fields on elements.
+#show list: it => {
+ test(it.children.len(), 3)
}
- A
@@ -31,8 +31,8 @@
#false.ok
---
-// Error: 29-32 content does not contain field "fun"
-#show heading: node => node.fun
+// Error: 25-28 content does not contain field "fun"
+#show heading: it => it.fun
= A
---
diff --git a/tests/typ/compiler/show-node.typ b/tests/typ/compiler/show-node.typ
index 0c1c1f52..99a4364e 100644
--- a/tests/typ/compiler/show-node.typ
+++ b/tests/typ/compiler/show-node.typ
@@ -1,4 +1,4 @@
-// Test node show rules.
+// Test show rules.
---
// Override lists.
@@ -83,12 +83,12 @@ Another text.
= Heading
---
-// Error: 7-12 this function is not selectable
-#show upper: it => {}
+#show text: none
+Hey
---
-// Error: 7-11 to select text, please use a string or regex instead
-#show text: it => {}
+// Error: 7-12 only element functions can be used as selectors
+#show upper: it => {}
---
// Error: 16-20 expected content or function, found integer
@@ -96,7 +96,7 @@ Another text.
= Heading
---
-// Error: 7-10 expected string, label, function, regular expression, or selector, found color
+// Error: 7-10 expected function, label, string, regular expression, or selector, found color
#show red: []
---
diff --git a/tests/typ/compute/construct.typ b/tests/typ/compute/construct.typ
index 80fa9a2b..dc146719 100644
--- a/tests/typ/compute/construct.typ
+++ b/tests/typ/compute/construct.typ
@@ -21,11 +21,11 @@
---
// Error for values that are out of range.
-// Error: 11-14 must be between 0 and 255
+// Error: 11-14 number must be between 0 and 255
#test(rgb(-30, 15, 50))
---
-// Error: 6-11 string contains non-hexadecimal letters
+// Error: 6-11 color string contains non-hexadecimal letters
#rgb("lol")
---
diff --git a/tests/typ/layout/list-marker.typ b/tests/typ/layout/list-marker.typ
index 0d223b58..97d1ce91 100644
--- a/tests/typ/layout/list-marker.typ
+++ b/tests/typ/layout/list-marker.typ
@@ -30,5 +30,5 @@
- a bad marker
---
-// Error: 19-21 must contain at least one marker
+// Error: 19-21 array must contain at least one marker
#set list(marker: ())
diff --git a/tests/typ/layout/pad.typ b/tests/typ/layout/pad.typ
index 2cc4d4ee..88e8510f 100644
--- a/tests/typ/layout/pad.typ
+++ b/tests/typ/layout/pad.typ
@@ -19,7 +19,7 @@ Hi #box(pad(left: 10pt)[A]) there
#pad(left: 10pt, right: 10pt)[PL #h(1fr) PR]
---
-// Test that the pad node doesn't consume the whole region.
+// Test that the pad element doesn't consume the whole region.
#set page(height: 6cm)
#align(left)[Before]
#pad(10pt, image("/tiger.jpg"))
diff --git a/tests/typ/layout/par-bidi.typ b/tests/typ/layout/par-bidi.typ
index d8c05d23..90c6a55e 100644
--- a/tests/typ/layout/par-bidi.typ
+++ b/tests/typ/layout/par-bidi.typ
@@ -51,5 +51,5 @@ Lריווח #h(1cm) R
// Test setting a vertical direction.
// Ref: false
-// Error: 16-19 must be horizontal
+// Error: 16-19 text direction must be horizontal
#set text(dir: ttb)
diff --git a/tests/typ/layout/place.typ b/tests/typ/layout/place.typ
index 18f7642d..f0ffaf57 100644
--- a/tests/typ/layout/place.typ
+++ b/tests/typ/layout/place.typ
@@ -7,7 +7,7 @@
= Placement
#place(right, image("/tiger.jpg", width: 1.8cm))
Hi there. This is \
-a placed node. \
+a placed element. \
Unfortunately, \
the line breaks still had to be inserted manually.
@@ -25,7 +25,7 @@ the line breaks still had to be inserted manually.
)
---
-// Test how the placed node interacts with paragraph spacing around it.
+// Test how the placed element interacts with paragraph spacing around it.
#set page("a8", height: 60pt)
First
diff --git a/tests/typ/meta/counter.typ b/tests/typ/meta/counter.typ
index c3b97629..a3814216 100644
--- a/tests/typ/meta/counter.typ
+++ b/tests/typ/meta/counter.typ
@@ -41,7 +41,7 @@ In #counter(heading).display().
At Beta, it was #locate(loc => {
let it = query(heading, loc).find(it => it.body == [Beta])
- numbering(it.numbering, ..counter(heading).at(it.id()))
+ numbering(it.numbering, ..counter(heading).at(it.location()))
})
---
diff --git a/tests/typ/meta/document.typ b/tests/typ/meta/document.typ
index f2c7a8bb..bc9d4908 100644
--- a/tests/typ/meta/document.typ
+++ b/tests/typ/meta/document.typ
@@ -19,23 +19,23 @@ What's up?
---
Hello
-// Error: 2-30 must appear before any content
+// Error: 2-30 document set rules must appear before any content
#set document(title: "Hello")
---
#box[
- // Error: 4-32 not allowed here
+ // Error: 4-32 document set rules are not allowed inside of containers
#set document(title: "Hello")
]
---
#box[
- // Error: 4-18 not allowed here
+ // Error: 4-18 page configuration is not allowed inside of containers
#set page("a4")
]
---
#box[
- // Error: 4-15 not allowed here
+ // Error: 4-15 pagebreaks are not allowed inside of containers
#pagebreak()
]
diff --git a/tests/typ/meta/query.typ b/tests/typ/meta/query.typ
index 38eadfab..8f8dcd3d 100644
--- a/tests/typ/meta/query.typ
+++ b/tests/typ/meta/query.typ
@@ -47,10 +47,10 @@
for it in elements [
Figure
#numbering(it.numbering,
- ..counter(figure).at(it.id())):
+ ..counter(figure).at(it.location())):
#it.caption
#box(width: 1fr, repeat[.])
- #counter(page).at(it.id()).first() \
+ #counter(page).at(it.location()).first() \
]
})
diff --git a/tests/typ/meta/state.typ b/tests/typ/meta/state.typ
index 8dfab05b..a7c3d26a 100644
--- a/tests/typ/meta/state.typ
+++ b/tests/typ/meta/state.typ
@@ -10,9 +10,9 @@ $ 2 + 3 $
#s.update(double)
Is: #s.display(),
-Was: #locate(id => {
- let it = query(math.equation, id).first()
- s.at(it.id())
+Was: #locate(location => {
+ let it = query(math.equation, location).first()
+ s.at(it.location())
}).
---
diff --git a/tests/typ/text/features.typ b/tests/typ/text/features.typ
index 360e8a11..69a7064f 100644
--- a/tests/typ/text/features.typ
+++ b/tests/typ/text/features.typ
@@ -47,7 +47,7 @@ fi vs. #text(features: (liga: 0))[No fi]
#set text(stylistic-set: false)
---
-// Error: 26-28 must be between 1 and 20
+// Error: 26-28 stylistic set must be between 1 and 20
#set text(stylistic-set: 25)
---
diff --git a/tests/typ/text/symbol.typ b/tests/typ/text/symbol.typ
index 949f82d2..f7179449 100644
--- a/tests/typ/text/symbol.typ
+++ b/tests/typ/text/symbol.typ
@@ -14,5 +14,5 @@
#sym.arrow.r;this and this#sym.arrow.l;
---
-// Error: 13-20 unknown modifier
+// Error: 13-20 unknown symbol modifier
#emoji.face.garbage