summaryrefslogtreecommitdiff
path: root/library/src/math/op.rs
blob: cc57eb6144ad4c0105f695a854dc1020c3afd05b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use typst::eval::Scope;

use super::*;

/// A text operator in an equation.
///
/// ## Example { #example }
/// ```example
/// $ tan x = (sin x)/(cos x) $
/// $ op("custom",
///      limits: #true)_(n->oo) n $
/// ```
///
/// ## Predefined Operators { #predefined }
/// Typst predefines the operators `arccos`,  `arcsin`,  `arctan`,  `arg`,
/// `cos`,  `cosh`,  `cot`, `ctg`, `coth`,  `csc`,  `deg`,  `det`,  `dim`,
/// `exp`, `gcd`,  `hom`,  `mod`,  `inf`,  `ker`,  `lg`,  `lim`,  `ln`,  `log`,
/// `max`, `min`,  `Pr`,  `sec`,  `sin`,  `sinc`,  `sinh`,  `sup`,  `tan`, `tg`,
/// `tanh`, `liminf`, and `limsup`.
///
/// Display: Text Operator
/// Category: math
#[element(LayoutMath)]
pub struct OpElem {
    /// The operator's text.
    #[required]
    pub text: EcoString,

    /// Whether the operator should force attachments to display as limits.
    #[default(false)]
    pub limits: bool,
}

impl LayoutMath for OpElem {
    #[tracing::instrument(skip(ctx))]
    fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
        let fragment =
            ctx.layout_text(&TextElem::new(self.text()).spanned(self.span()))?;
        ctx.push(
            FrameFragment::new(ctx, fragment.into_frame())
                .with_class(MathClass::Large)
                .with_limits(self.limits(ctx.styles())),
        );
        Ok(())
    }
}

macro_rules! ops {
    ($($name:ident $(: $value:literal)? $(($tts:tt))?),* $(,)?) => {
        pub(super) fn define(math: &mut Scope) {
            $(math.define(
                stringify!($name),
                OpElem::new(ops!(@name $name $(: $value)?).into())
                    .with_limits(ops!(@limit $($tts)*))
                    .pack()
            );)*

            let dif = |d| {
                HElem::new(THIN.into()).pack()
                    + MathStyleElem::new(TextElem::packed(d)).with_italic(Some(false)).pack()
            };
            math.define("dif", dif('d'));
            math.define("Dif", dif('D'));
        }
    };
    (@name $name:ident) => { stringify!($name) };
    (@name $name:ident: $value:literal) => { $value };
    (@limit limits) => { true };
    (@limit) => { false };
}

ops! {
    arccos,
    arcsin,
    arctan,
    arg,
    cos,
    cosh,
    cot,
    ctg,
    coth,
    csc,
    deg,
    det (limits),
    dim,
    exp,
    gcd (limits),
    hom,
    mod,
    inf (limits),
    ker,
    lg,
    lim (limits),
    ln,
    log,
    max (limits),
    min (limits),
    Pr (limits),
    sec,
    sin,
    sinc,
    sinh,
    sup (limits),
    tan,
    tg,
    tanh,
    liminf: "lim inf" (limits),
    limsup: "lim sup" (limits),
}