summaryrefslogtreecommitdiff
path: root/library/src/math
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
commit25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch)
tree2fbb4650903123da047a1f1f11a0abda95286e12 /library/src/math
parent6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff)
Fully untyped model
Diffstat (limited to 'library/src/math')
-rw-r--r--library/src/math/accent.rs118
-rw-r--r--library/src/math/align.rs18
-rw-r--r--library/src/math/attach.rs113
-rw-r--r--library/src/math/delimited.rs68
-rw-r--r--library/src/math/frac.rs71
-rw-r--r--library/src/math/matrix.rs120
-rw-r--r--library/src/math/mod.rs68
-rw-r--r--library/src/math/op.rs52
-rw-r--r--library/src/math/root.rs65
-rw-r--r--library/src/math/row.rs7
-rw-r--r--library/src/math/spacing.rs8
-rw-r--r--library/src/math/style.rs243
-rw-r--r--library/src/math/underover.rs182
13 files changed, 416 insertions, 717 deletions
diff --git a/library/src/math/accent.rs b/library/src/math/accent.rs
index 9c474eee..164247de 100644
--- a/library/src/math/accent.rs
+++ b/library/src/math/accent.rs
@@ -5,7 +5,6 @@ use super::*;
/// How much the accent can be shorter than the base.
const ACCENT_SHORT_FALL: Em = Em::new(0.5);
-/// # Accent
/// Attach an accent to a base.
///
/// ## Example
@@ -15,72 +14,48 @@ const ACCENT_SHORT_FALL: Em = Em::new(0.5);
/// $tilde(a) = accent(a, \u{0303})$
/// ```
///
-/// ## Parameters
-/// - base: `Content` (positional, required)
-/// The base to which the accent is applied.
-/// May consist of multiple letters.
-///
-/// ```example
-/// $arrow(A B C)$
-/// ```
-///
-/// - accent: `char` (positional, required)
-/// The accent to apply to the base.
-///
-/// Supported accents include:
-///
-/// | Accent | Name | Codepoint |
-/// | ------------ | --------------- | --------- |
-/// | Grave | `grave` | <code>&DiacriticalGrave;</code> |
-/// | Acute | `acute` | `´` |
-/// | Circumflex | `hat` | `^` |
-/// | Tilde | `tilde` | `~` |
-/// | Macron | `macron` | `¯` |
-/// | Breve | `breve` | `˘` |
-/// | Dot | `dot` | `.` |
-/// | Diaeresis | `diaer` | `¨` |
-/// | Circle | `circle` | `∘` |
-/// | Double acute | `acute.double` | `˝` |
-/// | Caron | `caron` | `ˇ` |
-/// | Right arrow | `arrow`, `->` | `→` |
-/// | Left arrow | `arrow.l`, `<-` | `←` |
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Accent
+/// Category: math
+#[node(LayoutMath)]
pub struct AccentNode {
- /// The accent base.
+ /// The base to which the accent is applied.
+ /// May consist of multiple letters.
+ ///
+ /// ```example
+ /// $arrow(A B C)$
+ /// ```
+ #[positional]
+ #[required]
pub base: Content,
- /// The accent.
- pub accent: char,
-}
-#[node]
-impl AccentNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let base = args.expect("base")?;
- let accent = args.expect::<Accent>("accent")?.0;
- Ok(Self { base, accent }.pack())
- }
-}
-
-struct Accent(char);
-
-castable! {
- Accent,
- v: char => Self(v),
- v: Content => match v.to::<TextNode>() {
- Some(text) => Self(Value::Str(text.0.clone().into()).cast()?),
- None => Err("expected text")?,
- },
+ /// The accent to apply to the base.
+ ///
+ /// Supported accents include:
+ ///
+ /// | Accent | Name | Codepoint |
+ /// | ------------ | --------------- | --------- |
+ /// | Grave | `grave` | <code>&DiacriticalGrave;</code> |
+ /// | Acute | `acute` | `´` |
+ /// | Circumflex | `hat` | `^` |
+ /// | Tilde | `tilde` | `~` |
+ /// | Macron | `macron` | `¯` |
+ /// | Breve | `breve` | `˘` |
+ /// | Dot | `dot` | `.` |
+ /// | Diaeresis | `diaer` | `¨` |
+ /// | Circle | `circle` | `∘` |
+ /// | Double acute | `acute.double` | `˝` |
+ /// | Caron | `caron` | `ˇ` |
+ /// | Right arrow | `arrow`, `->` | `→` |
+ /// | Left arrow | `arrow.l`, `<-` | `←` |
+ #[positional]
+ #[required]
+ pub accent: Accent,
}
impl LayoutMath for AccentNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_cramped(true));
- let base = ctx.layout_fragment(&self.base)?;
+ let base = ctx.layout_fragment(&self.base())?;
ctx.unstyle();
let base_attach = match &base {
@@ -92,7 +67,7 @@ impl LayoutMath for AccentNode {
// Forcing the accent to be at least as large as the base makes it too
// wide in many case.
- let c = combining_accent(self.accent).unwrap_or(self.accent);
+ let Accent(c) = self.accent();
let glyph = GlyphFragment::new(ctx, c);
let short_fall = ACCENT_SHORT_FALL.scaled(ctx);
let variant = glyph.stretch_horizontal(ctx, base.width(), short_fall);
@@ -136,3 +111,26 @@ fn attachment(ctx: &MathContext, id: GlyphId, italics_correction: Abs) -> Abs {
(advance.scaled(ctx) + italics_correction) / 2.0
})
}
+
+/// An accent character.
+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))
+ }
+}
+
+cast_from_value! {
+ Accent,
+ v: char => Self::new(v),
+ v: Content => match v.to::<TextNode>() {
+ Some(node) => Value::Str(node.text().into()).cast()?,
+ None => Err("expected text")?,
+ },
+}
+
+cast_to_value! {
+ v: Accent => v.0.into()
+}
diff --git a/library/src/math/align.rs b/library/src/math/align.rs
index a9005dfd..6cf13a0f 100644
--- a/library/src/math/align.rs
+++ b/library/src/math/align.rs
@@ -1,21 +1,11 @@
use super::*;
-/// # Alignment Point
/// A math alignment point: `&`, `&&`.
///
-/// ## Parameters
-/// - index: `usize` (positional, required)
-/// The alignment point's index.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct AlignPointNode;
-
-#[node]
-impl AlignPointNode {}
+/// Display: Alignment Point
+/// Category: math
+#[node(LayoutMath)]
+pub struct AlignPointNode {}
impl LayoutMath for AlignPointNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
diff --git a/library/src/math/attach.rs b/library/src/math/attach.rs
index 31dc75ab..9181ab7c 100644
--- a/library/src/math/attach.rs
+++ b/library/src/math/attach.rs
@@ -1,6 +1,5 @@
use super::*;
-/// # Attachment
/// A base with optional attachments.
///
/// ## Syntax
@@ -12,58 +11,44 @@ use super::*;
/// $ sum_(i=0)^n a_i = 2^(1+i) $
/// ```
///
-/// ## Parameters
-/// - base: `Content` (positional, required)
-/// The base to which things are attached.
-///
-/// - top: `Content` (named)
-/// The top attachment.
-///
-/// - bottom: `Content` (named)
-/// The bottom attachment.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Attachment
+/// Category: math
+#[node(LayoutMath)]
pub struct AttachNode {
- /// The base.
+ /// The base to which things are attached.
+ #[positional]
+ #[required]
pub base: Content,
+
/// The top attachment.
+ #[named]
+ #[default]
pub top: Option<Content>,
+
/// The bottom attachment.
+ #[named]
+ #[default]
pub bottom: Option<Content>,
}
-#[node]
-impl AttachNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let base = args.expect("base")?;
- let top = args.named("top")?;
- let bottom = args.named("bottom")?;
- Ok(Self { base, top, bottom }.pack())
- }
-}
-
impl LayoutMath for AttachNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let base = ctx.layout_fragment(&self.base)?;
+ let base = self.base();
+ let display_limits = base.is::<LimitsNode>();
+ let display_scripts = base.is::<ScriptsNode>();
+
+ let base = ctx.layout_fragment(&base)?;
ctx.style(ctx.style.for_subscript());
- let top = self.top.as_ref().map(|node| ctx.layout_fragment(node)).transpose()?;
+ let top = self.top().map(|node| ctx.layout_fragment(&node)).transpose()?;
ctx.unstyle();
ctx.style(ctx.style.for_superscript());
- let bottom = self
- .bottom
- .as_ref()
- .map(|node| ctx.layout_fragment(node))
- .transpose()?;
+ let bottom = self.bottom().map(|node| ctx.layout_fragment(&node)).transpose()?;
ctx.unstyle();
- let render_limits = self.base.is::<LimitsNode>()
- || (!self.base.is::<ScriptsNode>()
+ let display_limits = display_limits
+ || (!display_scripts
&& ctx.style.size == MathSize::Display
&& base.class() == Some(MathClass::Large)
&& match &base {
@@ -72,7 +57,7 @@ impl LayoutMath for AttachNode {
_ => false,
});
- if render_limits {
+ if display_limits {
limits(ctx, base, top, bottom)
} else {
scripts(ctx, base, top, bottom)
@@ -80,7 +65,6 @@ impl LayoutMath for AttachNode {
}
}
-/// # Scripts
/// Force a base to display attachments as scripts.
///
/// ## Example
@@ -88,31 +72,22 @@ impl LayoutMath for AttachNode {
/// $ scripts(sum)_1^2 != sum_1^2 $
/// ```
///
-/// ## Parameters
-/// - base: `Content` (positional, required)
-/// The base to attach the scripts to.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct ScriptsNode(Content);
-
-#[node]
-impl ScriptsNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("base")?).pack())
- }
+/// Display: Scripts
+/// Category: math
+#[node(LayoutMath)]
+pub struct ScriptsNode {
+ /// The base to attach the scripts to.
+ #[positional]
+ #[required]
+ pub base: Content,
}
impl LayoutMath for ScriptsNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- self.0.layout_math(ctx)
+ self.base().layout_math(ctx)
}
}
-/// # Limits
/// Force a base to display attachments as limits.
///
/// ## Example
@@ -120,27 +95,19 @@ impl LayoutMath for ScriptsNode {
/// $ limits(A)_1^2 != A_1^2 $
/// ```
///
-/// ## Parameters
-/// - base: `Content` (positional, required)
-/// The base to attach the limits to.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct LimitsNode(Content);
-
-#[node]
-impl LimitsNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("base")?).pack())
- }
+/// Display: Limits
+/// Category: math
+#[node(LayoutMath)]
+pub struct LimitsNode {
+ /// The base to attach the limits to.
+ #[positional]
+ #[required]
+ pub base: Content,
}
impl LayoutMath for LimitsNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- self.0.layout_math(ctx)
+ self.base().layout_math(ctx)
}
}
diff --git a/library/src/math/delimited.rs b/library/src/math/delimited.rs
index 511f1a7b..b0126cad 100644
--- a/library/src/math/delimited.rs
+++ b/library/src/math/delimited.rs
@@ -3,7 +3,6 @@ use super::*;
/// How much less high scaled delimiters can be than what they wrap.
pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1);
-/// # Left/Right
/// Scales delimiters.
///
/// While matched delimiters scale by default, this can be used to scale
@@ -24,20 +23,22 @@ pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1);
///
/// Defaults to `{100%}`.
///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Left/Right
+/// Category: math
+#[node(Construct, LayoutMath)]
pub struct LrNode {
/// The delimited content, including the delimiters.
+ #[positional]
+ #[required]
pub body: Content,
+
/// The size of the brackets.
- pub size: Option<Rel<Length>>,
+ #[named]
+ #[default]
+ pub size: Smart<Rel<Length>>,
}
-#[node]
-impl LrNode {
+impl Construct for LrNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let mut body = Content::empty();
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
@@ -46,21 +47,21 @@ impl LrNode {
}
body += arg;
}
- let size = args.named("size")?;
- Ok(Self { body, size }.pack())
+ let size = args.named::<Smart<Rel<Length>>>("size")?.unwrap_or_default();
+ Ok(Self::new(body).with_size(size).pack())
}
}
impl LayoutMath for LrNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let mut body = &self.body;
- if let Some(node) = self.body.to::<LrNode>() {
- if node.size.is_none() {
- body = &node.body;
+ let mut body = self.body();
+ if let Some(node) = body.to::<LrNode>() {
+ if node.size().is_auto() {
+ body = node.body();
}
}
- let mut fragments = ctx.layout_fragments(body)?;
+ let mut fragments = ctx.layout_fragments(&body)?;
let axis = scaled!(ctx, axis_height);
let max_extent = fragments
.iter()
@@ -69,7 +70,7 @@ impl LayoutMath for LrNode {
.unwrap_or_default();
let height = self
- .size
+ .size()
.unwrap_or(Rel::one())
.resolve(ctx.styles())
.relative_to(2.0 * max_extent);
@@ -116,7 +117,6 @@ fn scale(
}
}
-/// # Floor
/// Floor an expression.
///
/// ## Example
@@ -128,14 +128,13 @@ fn scale(
/// - body: `Content` (positional, required)
/// The expression to floor.
///
-/// ## Category
-/// math
+/// Display: Floor
+/// Category: math
#[func]
pub fn floor(args: &mut Args) -> SourceResult<Value> {
delimited(args, '⌊', '⌋')
}
-/// # Ceil
/// Ceil an expression.
///
/// ## Example
@@ -147,14 +146,13 @@ pub fn floor(args: &mut Args) -> SourceResult<Value> {
/// - body: `Content` (positional, required)
/// The expression to ceil.
///
-/// ## Category
-/// math
+/// Display: Ceil
+/// Category: math
#[func]
pub fn ceil(args: &mut Args) -> SourceResult<Value> {
delimited(args, '⌈', '⌉')
}
-/// # Abs
/// Take the absolute value of an expression.
///
/// ## Example
@@ -166,14 +164,13 @@ pub fn ceil(args: &mut Args) -> SourceResult<Value> {
/// - body: `Content` (positional, required)
/// The expression to take the absolute value of.
///
-/// ## Category
-/// math
+/// Display: Abs
+/// Category: math
#[func]
pub fn abs(args: &mut Args) -> SourceResult<Value> {
delimited(args, '|', '|')
}
-/// # Norm
/// Take the norm of an expression.
///
/// ## Example
@@ -185,8 +182,8 @@ pub fn abs(args: &mut Args) -> SourceResult<Value> {
/// - body: `Content` (positional, required)
/// The expression to take the norm of.
///
-/// ## Category
-/// math
+/// Display: Norm
+/// Category: math
#[func]
pub fn norm(args: &mut Args) -> SourceResult<Value> {
delimited(args, '‖', '‖')
@@ -194,14 +191,11 @@ pub fn norm(args: &mut Args) -> SourceResult<Value> {
fn delimited(args: &mut Args, left: char, right: char) -> SourceResult<Value> {
Ok(Value::Content(
- LrNode {
- body: Content::sequence(vec![
- TextNode::packed(left),
- args.expect::<Content>("body")?,
- TextNode::packed(right),
- ]),
- size: None,
- }
+ LrNode::new(Content::sequence(vec![
+ TextNode::packed(left),
+ args.expect::<Content>("body")?,
+ TextNode::packed(right),
+ ]))
.pack(),
))
}
diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs
index cdf533da..ea647fc5 100644
--- a/library/src/math/frac.rs
+++ b/library/src/math/frac.rs
@@ -17,41 +17,27 @@ const FRAC_AROUND: Em = Em::new(0.1);
/// expression using round grouping parenthesis. Such parentheses are removed
/// from the output, but you can nest multiple to force them.
///
-/// ## Parameters
-/// - num: `Content` (positional, required)
-/// The fraction's numerator.
-///
-/// - denom: `Content` (positional, required)
-/// The fraction's denominator.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Fraction
+/// Category: math
+#[node(LayoutMath)]
pub struct FracNode {
- /// The numerator.
+ /// The fraction's numerator.
+ #[positional]
+ #[required]
pub num: Content,
- /// The denominator.
- pub denom: Content,
-}
-#[node]
-impl FracNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let num = args.expect("numerator")?;
- let denom = args.expect("denominator")?;
- Ok(Self { num, denom }.pack())
- }
+ /// The fraction's denominator.
+ #[positional]
+ #[required]
+ pub denom: Content,
}
impl LayoutMath for FracNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.num, &self.denom, false)
+ layout(ctx, &self.num(), &self.denom(), false)
}
}
-/// # Binomial
/// A binomial expression.
///
/// ## Example
@@ -59,37 +45,24 @@ impl LayoutMath for FracNode {
/// $ binom(n, k) $
/// ```
///
-/// ## Parameters
-/// - upper: `Content` (positional, required)
-/// The binomial's upper index.
-///
-/// - lower: `Content` (positional, required)
-/// The binomial's lower index.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Binomial
+/// Category: math
+#[node(LayoutMath)]
pub struct BinomNode {
- /// The upper index.
+ /// The binomial's upper index.
+ #[positional]
+ #[required]
pub upper: Content,
- /// The lower index.
- pub lower: Content,
-}
-#[node]
-impl BinomNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let upper = args.expect("upper index")?;
- let lower = args.expect("lower index")?;
- Ok(Self { upper, lower }.pack())
- }
+ /// The binomial's lower index.
+ #[positional]
+ #[required]
+ pub lower: Content,
}
impl LayoutMath for BinomNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.upper, &self.lower, true)
+ layout(ctx, &self.upper(), &self.lower(), true)
}
}
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index 978d262b..3e257385 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -4,7 +4,6 @@ const ROW_GAP: Em = Em::new(0.5);
const COL_GAP: Em = Em::new(0.5);
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
-/// # Vector
/// A column vector.
///
/// Content in the vector's elements can be aligned with the `&` symbol.
@@ -15,41 +14,33 @@ const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
/// = a + 2b + 3c $
/// ```
///
-/// ## Parameters
-/// - elements: `Content` (positional, variadic)
-/// The elements of the vector.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct VecNode(Vec<Content>);
-
-#[node]
-impl VecNode {
+/// Display: Vector
+/// Category: math
+#[node(LayoutMath)]
+pub struct VecNode {
+ /// The elements of the vector.
+ #[variadic]
+ pub elements: Vec<Content>,
+
/// The delimiter to use.
///
/// ```example
/// #set math.vec(delim: "[")
/// $ vec(1, 2) $
/// ```
- pub const DELIM: Delimiter = Delimiter::Paren;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.all()?).pack())
- }
+ #[settable]
+ #[default(Delimiter::Paren)]
+ pub delim: Delimiter,
}
impl LayoutMath for VecNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = ctx.styles().get(Self::DELIM);
- let frame = layout_vec_body(ctx, &self.0, Align::Center)?;
+ let frame = layout_vec_body(ctx, &self.elements(), Align::Center)?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
}
-/// # Matrix
/// A matrix.
///
/// The elements of a row should be separated by commas, while the rows
@@ -70,33 +61,32 @@ impl LayoutMath for VecNode {
/// ) $
/// ```
///
-/// ## Parameters
-/// - rows: `Array` (positional, variadic)
-/// An array of arrays with the rows of the matrix.
-///
-/// ```example
-/// #let data = ((1, 2, 3), (4, 5, 6))
-/// #let matrix = math.mat(..data)
-/// $ v := matrix $
-/// ```
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct MatNode(Vec<Vec<Content>>);
-
-#[node]
-impl MatNode {
+/// Display: Matrix
+/// Category: math
+#[node(Construct, LayoutMath)]
+pub struct MatNode {
+ /// An array of arrays with the rows of the matrix.
+ ///
+ /// ```example
+ /// #let data = ((1, 2, 3), (4, 5, 6))
+ /// #let matrix = math.mat(..data)
+ /// $ v := matrix $
+ /// ```
+ #[variadic]
+ pub rows: Vec<Vec<Content>>,
+
/// The delimiter to use.
///
/// ```example
/// #set math.mat(delim: "[")
/// $ mat(1, 2; 3, 4) $
/// ```
- pub const DELIM: Delimiter = Delimiter::Paren;
+ #[settable]
+ #[default(Delimiter::Paren)]
+ pub delim: Delimiter,
+}
+impl Construct for MatNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let mut rows = vec![];
let mut width = 0;
@@ -119,19 +109,18 @@ impl MatNode {
}
}
- Ok(Self(rows).pack())
+ Ok(Self::new(rows).pack())
}
}
impl LayoutMath for MatNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = ctx.styles().get(Self::DELIM);
- let frame = layout_mat_body(ctx, &self.0)?;
+ let frame = layout_mat_body(ctx, &self.rows())?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
}
-/// # Cases
/// A case distinction.
///
/// Content across different branches can be aligned with the `&` symbol.
@@ -146,36 +135,29 @@ impl LayoutMath for MatNode {
/// ) $
/// ```
///
-/// ## Parameters
-/// - branches: `Content` (positional, variadic)
-/// The branches of the case distinction.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct CasesNode(Vec<Content>);
-
-#[node]
-impl CasesNode {
+/// Display: Cases
+/// Category: math
+#[node(LayoutMath)]
+pub struct CasesNode {
+ /// The branches of the case distinction.
+ #[variadic]
+ pub branches: Vec<Content>,
+
/// The delimiter to use.
///
/// ```example
/// #set math.cases(delim: "[")
/// $ x = cases(1, 2) $
/// ```
- pub const DELIM: Delimiter = Delimiter::Brace;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.all()?).pack())
- }
+ #[settable]
+ #[default(Delimiter::Brace)]
+ pub delim: Delimiter,
}
impl LayoutMath for CasesNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let delim = ctx.styles().get(Self::DELIM);
- let frame = layout_vec_body(ctx, &self.0, Align::Left)?;
+ let frame = layout_vec_body(ctx, &self.branches(), Align::Left)?;
layout_delimiters(ctx, frame, Some(delim.open()), None)
}
}
@@ -214,7 +196,7 @@ impl Delimiter {
}
}
-castable! {
+cast_from_value! {
Delimiter,
/// Delimit with parentheses.
"(" => Self::Paren,
@@ -228,6 +210,16 @@ castable! {
"||" => Self::DoubleBar,
}
+cast_to_value! {
+ v: Delimiter => Value::from(match v {
+ Delimiter::Paren => "(",
+ Delimiter::Bracket => "[",
+ Delimiter::Brace => "{",
+ Delimiter::Bar => "|",
+ Delimiter::DoubleBar => "||",
+ })
+}
+
/// Layout the inner contents of a vector.
fn layout_vec_body(
ctx: &mut MathContext,
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index d73b1769..9da12e4f 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -107,7 +107,6 @@ pub fn module() -> Module {
Module::new("math").with_scope(math)
}
-/// # Formula
/// A mathematical formula.
///
/// Can be displayed inline with text or as a separate block.
@@ -132,46 +131,25 @@ pub fn module() -> Module {
/// horizontally. For more details about math syntax, see the
/// [main math page]($category/math).
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The contents of the formula.
-///
-/// - block: `bool` (named)
-/// Whether the formula is displayed as a separate block.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(Show, Finalize, Layout, LayoutMath)]
-#[derive(Debug, Clone, Hash)]
+/// Display: Formula
+/// Category: math
+#[node(Show, Finalize, Layout, LayoutMath)]
pub struct FormulaNode {
- /// Whether the formula is displayed as a separate block.
- pub block: bool,
/// The content of the formula.
+ #[positional]
+ #[required]
pub body: Content,
-}
-
-#[node]
-impl FormulaNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let body = args.expect("body")?;
- let block = args.named("block")?.unwrap_or(false);
- Ok(Self { block, body }.pack())
- }
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "body" => Some(Value::Content(self.body.clone())),
- "block" => Some(Value::Bool(self.block)),
- _ => None,
- }
- }
+ /// Whether the formula is displayed as a separate block.
+ #[named]
+ #[default(false)]
+ pub block: bool,
}
impl Show for FormulaNode {
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
let mut realized = self.clone().pack().guarded(Guard::Base(NodeId::of::<Self>()));
- if self.block {
+ if self.block() {
realized = realized.aligned(Axes::with_x(Some(Align::Center.into())))
}
Ok(realized)
@@ -196,27 +174,29 @@ impl Layout for FormulaNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
+ let block = self.block();
+
// Find a math font.
let variant = variant(styles);
let world = vt.world();
let Some(font) = families(styles)
.find_map(|family| {
- let id = world.book().select(family, variant)?;
+ let id = world.book().select(family.as_str(), variant)?;
let font = world.font(id)?;
let _ = font.ttf().tables().math?.constants?;
Some(font)
})
else {
- if let Some(span) = self.body.span() {
+ if let Some(span) = self.span() {
bail!(span, "current font does not support math");
}
return Ok(Fragment::frame(Frame::new(Size::zero())))
};
- let mut ctx = MathContext::new(vt, styles, regions, &font, self.block);
+ let mut ctx = MathContext::new(vt, styles, regions, &font, block);
let mut frame = ctx.layout_frame(self)?;
- if !self.block {
+ if !block {
let slack = styles.get(ParNode::LEADING) * 0.7;
let top_edge = styles.get(TextNode::TOP_EDGE).resolve(styles, font.metrics());
let bottom_edge =
@@ -232,38 +212,38 @@ impl Layout for FormulaNode {
}
}
-#[capability]
pub trait LayoutMath {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()>;
}
impl LayoutMath for FormulaNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- self.body.layout_math(ctx)
+ self.body().layout_math(ctx)
}
}
impl LayoutMath for Content {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
if let Some(node) = self.to::<SequenceNode>() {
- for child in &node.0 {
+ for child in node.children() {
child.layout_math(ctx)?;
}
return Ok(());
}
if let Some(styled) = self.to::<StyledNode>() {
- if styled.map.contains(TextNode::FAMILY) {
+ let map = styled.map();
+ if map.contains(TextNode::FAMILY) {
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, styled.map.clone());
+ let prev_map = std::mem::replace(&mut ctx.map, map);
let prev_size = ctx.size;
ctx.map.apply(prev_map.clone());
ctx.size = ctx.styles().get(TextNode::SIZE);
- styled.sub.layout_math(ctx)?;
+ styled.sub().layout_math(ctx)?;
ctx.size = prev_size;
ctx.map = prev_map;
return Ok(());
@@ -280,7 +260,7 @@ impl LayoutMath for Content {
}
if let Some(node) = self.to::<HNode>() {
- if let Spacing::Rel(rel) = node.amount {
+ if let Spacing::Rel(rel) = node.amount() {
if rel.rel.is_zero() {
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
}
@@ -289,7 +269,7 @@ impl LayoutMath for Content {
}
if let Some(node) = self.to::<TextNode>() {
- ctx.layout_text(&node.0)?;
+ ctx.layout_text(&node.text())?;
return Ok(());
}
diff --git a/library/src/math/op.rs b/library/src/math/op.rs
index 4eb9c48c..c855cd92 100644
--- a/library/src/math/op.rs
+++ b/library/src/math/op.rs
@@ -2,7 +2,6 @@ use typst::eval::Scope;
use super::*;
-/// # Text Operator
/// A text operator in a math formula.
///
/// ## Example
@@ -19,45 +18,30 @@ use super::*;
/// `max`, `min`, `Pr`, `sec`, `sin`, `sinh`, `sup`, `tan`, `tg`, `tanh`,
/// `liminf`, and `limsup`.
///
-/// ## Parameters
-/// - text: `EcoString` (positional, required)
-/// The operator's text.
-///
-/// - limits: `bool` (named)
-/// Whether the operator should force attachments to display as limits.
-///
-/// Defaults to `{false}`.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Text Operator
+/// Category: math
+#[node(LayoutMath)]
pub struct OpNode {
/// The operator's text.
+ #[positional]
+ #[required]
pub text: EcoString,
+
/// Whether the operator should force attachments to display as limits.
+ ///
+ /// Defaults to `{false}`.
+ #[named]
+ #[default(false)]
pub limits: bool,
}
-#[node]
-impl OpNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- text: args.expect("text")?,
- limits: args.named("limits")?.unwrap_or(false),
- }
- .pack())
- }
-}
-
impl LayoutMath for OpNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let frame = ctx.layout_content(&TextNode(self.text.clone()).pack())?;
+ let frame = ctx.layout_content(&TextNode::packed(self.text()))?;
ctx.push(
FrameFragment::new(ctx, frame)
.with_class(MathClass::Large)
- .with_limits(self.limits),
+ .with_limits(self.limits()),
);
Ok(())
}
@@ -68,13 +52,15 @@ macro_rules! ops {
pub(super) fn define(math: &mut Scope) {
$(math.define(
stringify!($name),
- OpNode {
- text: ops!(@name $name $(: $value)?).into(),
- limits: ops!(@limit $($tts)*),
- }.pack()
+ OpNode::new(ops!(@name $name $(: $value)?).into())
+ .with_limits(ops!(@limit $($tts)*))
+ .pack()
);)*
- let dif = |d| HNode::strong(THIN).pack() + UprightNode(TextNode::packed(d)).pack();
+ let dif = |d| {
+ HNode::new(THIN.into()).pack()
+ + UprightNode::new(TextNode::packed(d)).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 e40f56f0..191acb94 100644
--- a/library/src/math/root.rs
+++ b/library/src/math/root.rs
@@ -1,6 +1,5 @@
use super::*;
-/// # Square Root
/// A square root.
///
/// ## Example
@@ -8,31 +7,22 @@ use super::*;
/// $ sqrt(x^2) = x = sqrt(x)^2 $
/// ```
///
-/// ## Parameters
-/// - radicand: `Content` (positional, required)
-/// The expression to take the square root of.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct SqrtNode(pub Content);
-
-#[node]
-impl SqrtNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("radicand")?).pack())
- }
+/// Display: Square Root
+/// Category: math
+#[node(LayoutMath)]
+pub struct SqrtNode {
+ /// The expression to take the square root of.
+ #[positional]
+ #[required]
+ pub radicand: Content,
}
impl LayoutMath for SqrtNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, None, &self.0)
+ layout(ctx, None, &self.radicand())
}
}
-/// # Root
/// A general root.
///
/// ## Example
@@ -40,37 +30,24 @@ impl LayoutMath for SqrtNode {
/// $ root(3, x) $
/// ```
///
-/// ## Parameters
-/// - index: `Content` (positional, required)
-/// Which root of the radicand to take.
-///
-/// - radicand: `Content` (positional, required)
-/// The expression to take the root of.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Root
+/// Category: math
+#[node(LayoutMath)]
pub struct RootNode {
+ /// Which root of the radicand to take.
+ #[positional]
+ #[required]
index: Content,
- radicand: Content,
-}
-#[node]
-impl RootNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- index: args.expect("index")?,
- radicand: args.expect("radicand")?,
- }
- .pack())
- }
+ /// The expression to take the root of.
+ #[positional]
+ #[required]
+ radicand: Content,
}
impl LayoutMath for RootNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, Some(&self.index), &self.radicand)
+ layout(ctx, Some(&self.index()), &self.radicand())
}
}
@@ -164,7 +141,7 @@ 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.0.as_str() {
+ let c = match node.text().as_str() {
"3" => '∛',
"4" => '∜',
_ => return None,
diff --git a/library/src/math/row.rs b/library/src/math/row.rs
index 1271e49e..b7720c14 100644
--- a/library/src/math/row.rs
+++ b/library/src/math/row.rs
@@ -103,7 +103,7 @@ impl MathRow {
pub fn to_frame(self, ctx: &MathContext) -> Frame {
let styles = ctx.styles();
- let align = styles.get(AlignNode::ALIGNS).x.resolve(styles);
+ let align = styles.get(AlignNode::ALIGNMENT).x.resolve(styles);
self.to_aligned_frame(ctx, &[], align)
}
@@ -200,10 +200,7 @@ impl MathRow {
}
}
-impl<T> From<T> for MathRow
-where
- T: Into<MathFragment>,
-{
+impl<T: Into<MathFragment>> From<T> for MathRow {
fn from(fragment: T) -> Self {
Self(vec![fragment.into()])
}
diff --git a/library/src/math/spacing.rs b/library/src/math/spacing.rs
index 17267238..e1b9d408 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::strong(THIN).pack());
- math.define("med", HNode::strong(MEDIUM).pack());
- math.define("thick", HNode::strong(THICK).pack());
- math.define("quad", HNode::strong(QUAD).pack());
+ 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());
}
/// 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 9856a6b0..99365106 100644
--- a/library/src/math/style.rs
+++ b/library/src/math/style.rs
@@ -1,6 +1,5 @@
use super::*;
-/// # Bold
/// Bold font style in math.
///
/// ## Example
@@ -8,34 +7,25 @@ use super::*;
/// $ bold(A) := B^+ $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct BoldNode(pub Content);
-
-#[node]
-impl BoldNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Bold
+/// Category: math
+#[node(LayoutMath)]
+pub struct BoldNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for BoldNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_bold(true));
- self.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Upright
/// Upright (non-italic) font style in math.
///
/// ## Example
@@ -43,98 +33,71 @@ impl LayoutMath for BoldNode {
/// $ upright(A) != A $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct UprightNode(pub Content);
-
-#[node]
-impl UprightNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Upright
+/// Category: math
+#[node(LayoutMath)]
+pub struct UprightNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for UprightNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_italic(false));
- self.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Italic
/// Italic font style in math.
///
/// For roman letters and greek lowercase letters, this is already the default.
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct ItalicNode(pub Content);
-
-#[node]
-impl ItalicNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Italic
+/// Category: math
+#[node(LayoutMath)]
+pub struct ItalicNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for ItalicNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_italic(true));
- self.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Serif
/// Serif (roman) font style in math.
///
/// This is already the default.
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct SerifNode(pub Content);
-
-#[node]
-impl SerifNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Serif
+/// Category: math
+#[node(LayoutMath)]
+pub struct SerifNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[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.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Sans-serif
/// Sans-serif font style in math.
///
/// ## Example
@@ -142,34 +105,25 @@ impl LayoutMath for SerifNode {
/// $ sans(A B C) $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct SansNode(pub Content);
-
-#[node]
-impl SansNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Sans-serif
+/// Category: math
+#[node(LayoutMath)]
+pub struct SansNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[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.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Calligraphic
/// Calligraphic font style in math.
///
/// ## Example
@@ -177,34 +131,25 @@ impl LayoutMath for SansNode {
/// Let $cal(P)$ be the set of ...
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct CalNode(pub Content);
-
-#[node]
-impl CalNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Calligraphic
+/// Category: math
+#[node(LayoutMath)]
+pub struct CalNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[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.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Fraktur
/// Fraktur font style in math.
///
/// ## Example
@@ -212,34 +157,25 @@ impl LayoutMath for CalNode {
/// $ frak(P) $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct FrakNode(pub Content);
-
-#[node]
-impl FrakNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Fraktur
+/// Category: math
+#[node(LayoutMath)]
+pub struct FrakNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[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.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Monospace
/// Monospace font style in math.
///
/// ## Example
@@ -247,34 +183,25 @@ impl LayoutMath for FrakNode {
/// $ mono(x + y = z) $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct MonoNode(pub Content);
-
-#[node]
-impl MonoNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Monospace
+/// Category: math
+#[node(LayoutMath)]
+pub struct MonoNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[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.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
}
-/// # Blackboard Bold
/// Blackboard bold (double-struck) font style in math.
///
/// For uppercase latin letters, blackboard bold is additionally available
@@ -287,28 +214,20 @@ impl LayoutMath for MonoNode {
/// $ f: NN -> RR $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The piece of formula to style.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct BbNode(pub Content);
-
-#[node]
-impl BbNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Blackboard Bold
+/// Category: math
+#[node(LayoutMath)]
+pub struct BbNode {
+ /// The piece of formula to style.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for BbNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
ctx.style(ctx.style.with_variant(MathVariant::Bb));
- self.0.layout_math(ctx)?;
+ self.body().layout_math(ctx)?;
ctx.unstyle();
Ok(())
}
diff --git a/library/src/math/underover.rs b/library/src/math/underover.rs
index 4387de20..87f30c0f 100644
--- a/library/src/math/underover.rs
+++ b/library/src/math/underover.rs
@@ -4,7 +4,6 @@ const LINE_GAP: Em = Em::new(0.15);
const BRACE_GAP: Em = Em::new(0.25);
const BRACKET_GAP: Em = Em::new(0.25);
-/// # Underline
/// A horizontal line under content.
///
/// ## Example
@@ -12,31 +11,22 @@ const BRACKET_GAP: Em = Em::new(0.25);
/// $ underline(1 + 2 + ... + 5) $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content above the line.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct UnderlineNode(Content);
-
-#[node]
-impl UnderlineNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Underline
+/// Category: math
+#[node(LayoutMath)]
+pub struct UnderlineNode {
+ /// The content above the line.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for UnderlineNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.0, &None, '\u{305}', LINE_GAP, false)
+ layout(ctx, &self.body(), &None, '\u{305}', LINE_GAP, false)
}
}
-/// # Overline
/// A horizontal line over content.
///
/// ## Example
@@ -44,31 +34,22 @@ impl LayoutMath for UnderlineNode {
/// $ overline(1 + 2 + ... + 5) $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content below the line.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
-pub struct OverlineNode(Content);
-
-#[node]
-impl OverlineNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("body")?).pack())
- }
+/// Display: Overline
+/// Category: math
+#[node(LayoutMath)]
+pub struct OverlineNode {
+ /// The content below the line.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl LayoutMath for OverlineNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.0, &None, '\u{332}', LINE_GAP, true)
+ layout(ctx, &self.body(), &None, '\u{332}', LINE_GAP, true)
}
}
-/// # Underbrace
/// A horizontal brace under content, with an optional annotation below.
///
/// ## Example
@@ -76,41 +57,27 @@ impl LayoutMath for OverlineNode {
/// $ underbrace(1 + 2 + ... + 5, "numbers") $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content above the brace.
-///
-/// - annotation: `Content` (positional)
-/// The optional content below the brace.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Underbrace
+/// Category: math
+#[node(LayoutMath)]
pub struct UnderbraceNode {
/// The content above the brace.
+ #[positional]
+ #[required]
pub body: Content,
+
/// The optional content below the brace.
+ #[positional]
+ #[default]
pub annotation: Option<Content>,
}
-#[node]
-impl UnderbraceNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let body = args.expect("body")?;
- let annotation = args.eat()?;
- Ok(Self { body, annotation }.pack())
- }
-}
-
impl LayoutMath for UnderbraceNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body, &self.annotation, '⏟', BRACE_GAP, false)
+ layout(ctx, &self.body(), &self.annotation(), '⏟', BRACE_GAP, false)
}
}
-/// # Overbrace
/// A horizontal brace over content, with an optional annotation above.
///
/// ## Example
@@ -118,41 +85,27 @@ impl LayoutMath for UnderbraceNode {
/// $ overbrace(1 + 2 + ... + 5, "numbers") $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content below the brace.
-///
-/// - annotation: `Content` (positional)
-/// The optional content above the brace.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Overbrace
+/// Category: math
+#[node(LayoutMath)]
pub struct OverbraceNode {
/// The content below the brace.
+ #[positional]
+ #[required]
pub body: Content,
+
/// The optional content above the brace.
+ #[positional]
+ #[default]
pub annotation: Option<Content>,
}
-#[node]
-impl OverbraceNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let body = args.expect("body")?;
- let annotation = args.eat()?;
- Ok(Self { body, annotation }.pack())
- }
-}
-
impl LayoutMath for OverbraceNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body, &self.annotation, '⏞', BRACE_GAP, true)
+ layout(ctx, &self.body(), &self.annotation(), '⏞', BRACE_GAP, true)
}
}
-/// # Underbracket
/// A horizontal bracket under content, with an optional annotation below.
///
/// ## Example
@@ -160,41 +113,27 @@ impl LayoutMath for OverbraceNode {
/// $ underbracket(1 + 2 + ... + 5, "numbers") $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content above the bracket.
-///
-/// - annotation: `Content` (positional)
-/// The optional content below the bracket.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Underbracket
+/// Category: math
+#[node(LayoutMath)]
pub struct UnderbracketNode {
/// The content above the bracket.
+ #[positional]
+ #[required]
pub body: Content,
+
/// The optional content below the bracket.
+ #[positional]
+ #[default]
pub annotation: Option<Content>,
}
-#[node]
-impl UnderbracketNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let body = args.expect("body")?;
- let annotation = args.eat()?;
- Ok(Self { body, annotation }.pack())
- }
-}
-
impl LayoutMath for UnderbracketNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body, &self.annotation, '⎵', BRACKET_GAP, false)
+ layout(ctx, &self.body(), &self.annotation(), '⎵', BRACKET_GAP, false)
}
}
-/// # Overbracket
/// A horizontal bracket over content, with an optional annotation above.
///
/// ## Example
@@ -202,37 +141,24 @@ impl LayoutMath for UnderbracketNode {
/// $ overbracket(1 + 2 + ... + 5, "numbers") $
/// ```
///
-/// ## Parameters
-/// - body: `Content` (positional, required)
-/// The content below the bracket.
-///
-/// - annotation: `Content` (positional)
-/// The optional content above the bracket.
-///
-/// ## Category
-/// math
-#[func]
-#[capable(LayoutMath)]
-#[derive(Debug, Hash)]
+/// Display: Overbracket
+/// Category: math
+#[node(LayoutMath)]
pub struct OverbracketNode {
/// The content below the bracket.
+ #[positional]
+ #[required]
pub body: Content,
+
/// The optional content above the bracket.
+ #[positional]
+ #[default]
pub annotation: Option<Content>,
}
-#[node]
-impl OverbracketNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let body = args.expect("body")?;
- let annotation = args.eat()?;
- Ok(Self { body, annotation }.pack())
- }
-}
-
impl LayoutMath for OverbracketNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body, &self.annotation, '⎴', BRACKET_GAP, true)
+ layout(ctx, &self.body(), &self.annotation(), '⎴', BRACKET_GAP, true)
}
}