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()
}