diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
| commit | 25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch) | |
| tree | 2fbb4650903123da047a1f1f11a0abda95286e12 /library/src/math | |
| parent | 6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff) | |
Fully untyped model
Diffstat (limited to 'library/src/math')
| -rw-r--r-- | library/src/math/accent.rs | 118 | ||||
| -rw-r--r-- | library/src/math/align.rs | 18 | ||||
| -rw-r--r-- | library/src/math/attach.rs | 113 | ||||
| -rw-r--r-- | library/src/math/delimited.rs | 68 | ||||
| -rw-r--r-- | library/src/math/frac.rs | 71 | ||||
| -rw-r--r-- | library/src/math/matrix.rs | 120 | ||||
| -rw-r--r-- | library/src/math/mod.rs | 68 | ||||
| -rw-r--r-- | library/src/math/op.rs | 52 | ||||
| -rw-r--r-- | library/src/math/root.rs | 65 | ||||
| -rw-r--r-- | library/src/math/row.rs | 7 | ||||
| -rw-r--r-- | library/src/math/spacing.rs | 8 | ||||
| -rw-r--r-- | library/src/math/style.rs | 243 | ||||
| -rw-r--r-- | library/src/math/underover.rs | 182 |
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>`</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>`</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) } } |
