typst_macros/
lib.rs

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
//! Procedural macros for Typst.

extern crate proc_macro;

#[macro_use]
mod util;
mod cast;
mod category;
mod elem;
mod func;
mod scope;
mod symbols;
mod time;
mod ty;

use proc_macro::TokenStream as BoundaryStream;
use syn::DeriveInput;

/// Makes a native Rust function usable as a Typst function.
///
/// This implements `NativeFunction` for a freshly generated type with the same
/// name as a function. (In Rust, functions and types live in separate
/// namespace, so both can coexist.)
///
/// If the function is in an impl block annotated with `#[scope]`, things work a
/// bit differently because the no type can be generated within the impl block.
/// In that case, a function named `{name}_data` that returns `&'static
/// NativeFuncData` is generated. You typically don't need to interact with this
/// function though because the `#[scope]` macro hooks everything up for you.
///
/// ```ignore
/// /// Doubles an integer.
/// #[func]
/// fn double(x: i64) -> i64 {
///     2 * x
/// }
/// ```
///
/// # Properties
/// You can customize some properties of the resulting function:
/// - `scope`: Indicates that the function has an associated scope defined by
///   the `#[scope]` macro.
/// - `contextual`: Indicates that the function makes use of context. This has
///   no effect on the behaviour itself, but is used for the docs.
/// - `name`: The functions's normal name (e.g. `min`), as exposed to Typst.
///   Defaults to the Rust name in kebab-case.
/// - `title`: The functions's title case name (e.g. `Minimum`). Defaults to the
///   normal name in title case.
/// - `keywords = [..]`: A list of alternate search terms for this function.
/// - `constructor`: Indicates that the function is a constructor.
///
/// # Arguments
/// By default, function arguments are positional and required. You can use
/// various attributes to configure their parsing behaviour:
///
/// - `#[named]`: Makes the argument named and optional. The argument type must
///   either be `Option<_>` _or_ the `#[default]` attribute must be used. (If
///   it's both `Option<_>` and `#[default]`, then the argument can be specified
///   as `none` in Typst).
/// - `#[default]`: Specifies the default value of the argument as
///   `Default::default()`.
/// - `#[default(..)]`: Specifies the default value of the argument as `..`.
/// - `#[variadic]`: Parses a variable number of arguments. The argument type
///   must be `Vec<_>`.
/// - `#[external]`: The argument appears in documentation, but is otherwise
///   ignored. Can be useful if you want to do something manually for more
///   flexibility.
///
/// Defaults can be specified for positional and named arguments. This is in
/// contrast to user-defined functions which currently cannot have optional
/// positional arguments (except through argument sinks).
///
/// In the example below, we define a `min` function that could be called as
/// `min(1, 2, 3, default: 0)` in Typst.
///
/// ```ignore
/// /// Determines the minimum of a sequence of values.
/// #[func(title = "Minimum")]
/// fn min(
///     /// The values to extract the minimum from.
///     #[variadic]
///     values: Vec<i64>,
///     /// A default value to return if there are no values.
///     #[named]
///     #[default(0)]
///     default: i64,
/// ) -> i64 {
///     self.values.iter().min().unwrap_or(default)
/// }
/// ```
///
/// As you can see, arguments can also have doc-comments, which will be rendered
/// in the documentation. The first line of documentation should be concise and
/// self-contained as it is the designated short description, which is used in
/// overviews in the documentation (and for autocompletion).
///
/// Additionally, some arguments are treated specially by this macro:
///
/// - `engine`: The compilation context (`Engine`).
/// - `context`: The introspection context (`Tracked<Context>`).
/// - `args`: The rest of the arguments passed into this function (`&mut Args`).
/// - `span`: The span of the function call (`Span`).
///
/// These should always come after `self`, in the order specified.
#[proc_macro_attribute]
pub fn func(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::ItemFn);
    func::func(stream.into(), &item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Makes a native Rust type usable as a Typst type.
///
/// This implements `NativeType` for the given type.
///
/// ```ignore
/// /// A sequence of codepoints.
/// #[ty(scope, title = "String")]
/// struct Str(EcoString);
///
/// #[scope]
/// impl Str {
///     ...
/// }
/// ```
///
/// # Properties
/// You can customize some properties of the resulting type:
/// - `scope`: Indicates that the type has an associated scope defined by the
///   `#[scope]` macro
/// - `cast`: Indicates that the type has a custom `cast!` implementation.
///   The macro will then not autogenerate one.
/// - `name`: The type's normal name (e.g. `str`), as exposed to Typst.
///   Defaults to the Rust name in kebab-case.
/// - `title`: The type's title case name (e.g. `String`). Defaults to the
///   normal name in title case.
#[proc_macro_attribute]
pub fn ty(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::Item);
    ty::ty(stream.into(), item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Makes a native Rust type usable as a Typst element.
///
/// This implements `NativeElement` for the given type.
///
/// ```ignore
/// /// A section heading.
/// #[elem(Show, Count)]
/// struct HeadingElem {
///     /// The logical nesting depth of the heading, starting from one.
///     #[default(NonZeroUsize::ONE)]
///     level: NonZeroUsize,
///
///     /// The heading's title.
///     #[required]
///     body: Content,
/// }
/// ```
///
/// # Properties
/// You can customize some properties of the resulting type:
/// - `scope`: Indicates that the type has an associated scope defined by the
///   `#[scope]` macro.
/// - `name = "<name>"`: The element's normal name (e.g. `align`), as exposed to Typst.
///   Defaults to the Rust name in kebab-case.
/// - `title = "<title>"`: The type's title case name (e.g. `Align`). Defaults to the long
///   name in title case.
/// - `keywords = [..]`: A list of alternate search terms for this element.
///   Defaults to the empty list.
/// - The remaining entries in the `elem` macros list are traits the element
///   is capable of. These can be dynamically accessed.
///
/// # Fields
/// By default, element fields are named and optional (and thus settable). You
/// can use various attributes to configure their parsing behaviour:
///
/// - `#[positional]`: Makes the argument positional (but still optional).
/// - `#[required]`: Makes the argument positional and required.
/// - `#[default(..)]`: Specifies the default value of the argument as `..`.
/// - `#[variadic]`: Parses a variable number of arguments. The field type must
///   be `Vec<_>`. The field will be exposed as an array.
/// - `#[parse({ .. })]`: A block of code that parses the field manually.
///
/// In addition that there are a number of attributes that configure other
/// aspects of the field than the parsing behaviour.
/// - `#[resolve]`: When accessing the field, it will be automatically
///   resolved through the `Resolve` trait. This, for instance, turns `Length`
///   into `Abs`. It's just convenient.
/// - `#[fold]`: When there are multiple set rules for the field, all values
///   are folded together into one. E.g. `set rect(stroke: 2pt)` and
///   `set rect(stroke: red)` are combined into the equivalent of
///   `set rect(stroke: 2pt + red)` instead of having `red` override `2pt`.
/// - `#[borrowed]`: For fields that are accessed through the style chain,
///   indicates that accessor methods to this field should return references
///   to the value instead of cloning.
/// - `#[internal]`: The field does not appear in the documentation.
/// - `#[external]`: The field appears in the documentation, but is otherwise
///   ignored. Can be useful if you want to do something manually for more
///   flexibility.
/// - `#[synthesized]`: The field cannot be specified in a constructor or set
///   rule. Instead, it is added to an element before its show rule runs
///   through the `Synthesize` trait.
/// - `#[ghost]`: Allows creating fields that are only present in the style chain,
///   this means that they *cannot* be accessed by the user, they cannot be set
///   on an individual instantiated element, and must be set via the style chain.
///   This is useful for fields that are only used internally by the style chain,
///   such as the fields from `ParElem` and `TextElem`. If your element contains
///   any ghost fields, then you cannot auto-generate `Construct` for it, and
///   you must implement `Construct` manually.
#[proc_macro_attribute]
pub fn elem(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::ItemStruct);
    elem::elem(stream.into(), item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Provides an associated scope to a native function, type, or element.
///
/// This implements `NativeScope` for the function's shadow type, the type, or
/// the element.
///
/// The implementation block can contain four kinds of items:
/// - constants, which will be defined through `scope.define`
/// - functions, which will be defined through `scope.define_func`
/// - types, which will be defined through `scope.define_type`
/// - elements, which will be defined through `scope.define_elem`
///
/// ```ignore
/// #[func(scope)]
/// fn name() { .. }
///
/// #[scope]
/// impl name {
///     /// A simple constant.
///     const VAL: u32 = 0;
///
///     /// A function.
///     #[func]
///     fn foo() -> EcoString {
///         "foo!".into()
///     }
///
///     /// A type.
///     type Brr;
///
///     /// An element.
///     #[elem]
///     type NiceElem;
/// }
///
/// #[ty]
/// struct Brr;
///
/// #[elem]
/// struct NiceElem {}
/// ```
#[proc_macro_attribute]
pub fn scope(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::Item);
    scope::scope(stream.into(), item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Defines a category of definitions.
#[proc_macro_attribute]
pub fn category(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::Item);
    category::category(stream.into(), item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implements `Reflect`, `FromValue`, and `IntoValue` for a type.
///
/// - `Reflect` makes Typst's runtime aware of the type's characteristics.
///   It's important for autocompletion, error messages, etc.
/// - `FromValue` defines how to cast from a value into this type.
/// - `IntoValue` defines how to cast fromthis type into a value.
///
/// ```ignore
/// /// An integer between 0 and 13.
/// struct CoolInt(u8);
///
/// cast! {
///     CoolInt,
///
///     // Defines how to turn a `CoolInt` into a value.
///     self => self.0.into_value(),
///
///     // Defines "match arms" of types that can be cast into a `CoolInt`.
///     // These types needn't be value primitives, they can themselves use
///     // `cast!`.
///     v: bool => Self(v as u8),
///     v: i64 => if matches!(v, 0..=13) {
///         Self(v as u8)
///     } else {
///         bail!("integer is not nice :/")
///     },
/// }
/// ```
#[proc_macro]
pub fn cast(stream: BoundaryStream) -> BoundaryStream {
    cast::cast(stream.into())
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implements `Reflect`, `FromValue`, and `IntoValue` for an enum.
///
/// The enum will become castable from kebab-case strings. The doc-comments will
/// become user-facing documentation for each variant. The `#[string]` attribute
/// can be used to override the string corresponding to a variant.
///
/// ```ignore
/// /// A stringy enum of options.
/// #[derive(Cast)]
/// enum Niceness {
///     /// Clearly nice (parses from `"nice"`).
///     Nice,
///     /// Not so nice (parses from `"not-nice"`).
///     NotNice,
///     /// Very much not nice (parses from `"โŒ"`).
///     #[string("โŒ")]
///     Unnice,
/// }
/// ```
#[proc_macro_derive(Cast, attributes(string))]
pub fn derive_cast(item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as DeriveInput);
    cast::derive_cast(item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Defines a list of `Symbol`s.
///
/// The `#[call(path)]` attribute can be used to specify a function to call when
/// the symbol is invoked. The function must be `NativeFunc`.
///
/// ```ignore
/// const EMOJI: &[(&str, Symbol)] = symbols! {
///     // A plain symbol without modifiers.
///     abacus: '๐Ÿงฎ',
///
///     // A symbol with a modifierless default and one modifier.
///     alien: ['๐Ÿ‘ฝ', monster: '๐Ÿ‘พ'],
///
///     // A symbol where each variant has a modifier. The first one will be
///     // the default.
///     clock: [one: '๐Ÿ•', two: '๐Ÿ•‘', ...],
///
///     // A callable symbol without modifiers.
///     breve: #[call(crate::math::breve)] 'ห˜',
///
///     // A callable symbol with a modifierless default and one modifier.
///     acute: [
///         #[call(crate::math::acute)] 'ยด',
///         double: 'ห',
///     ],
///
///     // A callable symbol where each variant has a modifier.
///     arrow: [
///         #[call(crate::math::arrow)] r: 'โ†’',
///         r.long.bar: 'โŸผ',
///         #[call(crate::math::arrow_l)] l: 'โ†',
///         l.long.bar: 'โŸป',
///     ],
/// }
/// ```
///
/// _Note:_ While this could use `macro_rules!` instead of a proc-macro, it was
/// horribly slow in rust-analyzer. The underlying cause might be
/// [this issue](https://github.com/rust-lang/rust-analyzer/issues/11108).
#[proc_macro]
pub fn symbols(stream: BoundaryStream) -> BoundaryStream {
    symbols::symbols(stream.into())
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Times function invocations.
///
/// When tracing is enabled in the typst-cli, this macro will record the
/// invocations of the function and store them in a global map. The map can be
/// accessed through the `typst_trace::RECORDER` static.
///
/// You can also specify the span of the function invocation:
/// - `#[time(span = ..)]` to record the span, which will be used for the
///   `EventKey`.
///
/// By default, all tracing is omitted using the `wasm32` target flag.
/// This is done to avoid bloating the web app, which doesn't need tracing.
///
/// ```ignore
/// #[time]
/// fn fibonacci(n: u64) -> u64 {
///     if n <= 1 {
///         1
///     } else {
///         fibonacci(n - 1) + fibonacci(n - 2)
///     }
/// }
///
/// #[time(span = span)]
/// fn fibonacci_spanned(n: u64, span: Span) -> u64 {
///     if n <= 1 {
///         1
///     } else {
///         fibonacci(n - 1) + fibonacci(n - 2)
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn time(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
    let item = syn::parse_macro_input!(item as syn::ItemFn);
    time::time(stream.into(), item)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}