summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/meta
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
committerLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
commitb471ac7d590abd2398ce25193b4e4df373bf2e9c (patch)
treeb5f7a6fdc807ee3340a4f42b0ad3cc563fe45429 /crates/typst-library/src/meta
parent8f36fca68447a5d42a3d54b5fac7e5546ee244be (diff)
First-class types
Makes types first-class values.
Diffstat (limited to 'crates/typst-library/src/meta')
-rw-r--r--crates/typst-library/src/meta/bibliography.rs37
-rw-r--r--crates/typst-library/src/meta/context.rs83
-rw-r--r--crates/typst-library/src/meta/counter.rs373
-rw-r--r--crates/typst-library/src/meta/document.rs5
-rw-r--r--crates/typst-library/src/meta/figure.rs82
-rw-r--r--crates/typst-library/src/meta/footnote.rs46
-rw-r--r--crates/typst-library/src/meta/heading.rs37
-rw-r--r--crates/typst-library/src/meta/link.rs26
-rw-r--r--crates/typst-library/src/meta/metadata.rs15
-rw-r--r--crates/typst-library/src/meta/mod.rs49
-rw-r--r--crates/typst-library/src/meta/numbering.rs9
-rw-r--r--crates/typst-library/src/meta/outline.rs73
-rw-r--r--crates/typst-library/src/meta/query.rs49
-rw-r--r--crates/typst-library/src/meta/reference.rs30
-rw-r--r--crates/typst-library/src/meta/state.rs236
15 files changed, 490 insertions, 660 deletions
diff --git a/crates/typst-library/src/meta/bibliography.rs b/crates/typst-library/src/meta/bibliography.rs
index d871db23..4f1cc32c 100644
--- a/crates/typst-library/src/meta/bibliography.rs
+++ b/crates/typst-library/src/meta/bibliography.rs
@@ -30,8 +30,8 @@ use crate::text::TextElem;
///
/// As soon as you add a bibliography somewhere in your document, you can start
/// citing things with reference syntax (`[@key]`) or explicit calls to the
-/// [citation]($func/cite) function (`[#cite("key")]`). The bibliography will
-/// only show entries for works that were referenced in the document.
+/// [citation]($cite) function (`[#cite("key")]`). The bibliography will only
+/// show entries for works that were referenced in the document.
///
/// # Example
/// ```example
@@ -43,10 +43,7 @@ use crate::text::TextElem;
///
/// #bibliography("works.bib")
/// ```
-///
-/// Display: Bibliography
-/// Category: meta
-#[element(Locatable, Synthesize, Show, Finalize, LocalName)]
+#[elem(Locatable, Synthesize, Show, Finalize, LocalName)]
pub struct BibliographyElem {
/// Path to a Hayagriva `.yml` or BibLaTeX `.bib` file.
#[required]
@@ -78,8 +75,8 @@ pub struct BibliographyElem {
/// The title of the bibliography.
///
- /// - When set to `{auto}`, an appropriate title for the [text
- /// language]($func/text.lang) will be used. This is the default.
+ /// - When set to `{auto}`, an appropriate title for the
+ /// [text language]($text.lang) will be used. This is the default.
/// - When set to `{none}`, the bibliography will not have a title.
/// - A custom title can be set by passing content.
///
@@ -109,7 +106,7 @@ cast! {
impl BibliographyElem {
/// Find the document's bibliography.
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
- let mut iter = introspector.query(&Self::func().select()).into_iter();
+ let mut iter = introspector.query(&Self::elem().select()).into_iter();
let Some(elem) = iter.next() else {
bail!("the document does not contain a bibliography");
};
@@ -124,7 +121,7 @@ impl BibliographyElem {
/// Whether the bibliography contains the given key.
pub fn has(vt: &Vt, key: &str) -> bool {
vt.introspector
- .query(&Self::func().select())
+ .query(&Self::elem().select())
.into_iter()
.flat_map(|elem| {
let elem = elem.to::<Self>().unwrap();
@@ -289,8 +286,8 @@ impl BibliographyStyle {
/// Cite a work from the bibliography.
///
-/// Before you starting citing, you need to add a
-/// [bibliography]($func/bibliography) somewhere in your document.
+/// Before you starting citing, you need to add a [bibliography]($bibliography)
+/// somewhere in your document.
///
/// # Example
/// ```example
@@ -304,13 +301,10 @@ impl BibliographyStyle {
/// ```
///
/// # Syntax
-/// This function indirectly has dedicated syntax. [References]($func/ref)
-/// can be used to cite works from the bibliography. The label then
-/// corresponds to the citation key.
-///
-/// Display: Citation
-/// Category: meta
-#[element(Locatable, Synthesize, Show)]
+/// This function indirectly has dedicated syntax. [References]($ref) can be
+/// used to cite works from the bibliography. The label then corresponds to the
+/// citation key.
+#[elem(Locatable, Synthesize, Show)]
pub struct CiteElem {
/// The citation keys that identify the elements that shall be cited in
/// the bibliography.
@@ -329,7 +323,6 @@ pub struct CiteElem {
///
/// #bibliography("works.bib")
/// ```
- #[positional]
pub supplement: Option<Content>,
/// Whether the citation should include brackets.
@@ -435,8 +428,8 @@ impl Works {
let citations = vt
.introspector
.query(&Selector::Or(eco_vec![
- RefElem::func().select(),
- CiteElem::func().select(),
+ RefElem::elem().select(),
+ CiteElem::elem().select(),
]))
.into_iter()
.map(|elem| match elem.to::<RefElem>() {
diff --git a/crates/typst-library/src/meta/context.rs b/crates/typst-library/src/meta/context.rs
index a42c6980..3a82a925 100644
--- a/crates/typst-library/src/meta/context.rs
+++ b/crates/typst-library/src/meta/context.rs
@@ -2,9 +2,9 @@ use crate::prelude::*;
/// Provides access to the location of content.
///
-/// This is useful in combination with [queries]($func/query),
-/// [counters]($func/counter), [state]($func/state), and [links]($func/link).
-/// See their documentation for more details.
+/// This is useful in combination with [queries]($query), [counters]($counter),
+/// [state]($state), and [links]($link). See their documentation for more
+/// details.
///
/// ```example
/// #locate(loc => [
@@ -12,44 +12,10 @@ use crate::prelude::*;
/// #loc.position()!
/// ])
/// ```
-///
-/// ## Methods
-/// ### page()
-/// Returns the page number for this location.
-///
-/// Note that this does not return the value of the [page counter]($func/counter)
-/// at this location, but the true page number (starting from one).
-///
-/// If you want to know the value of the page counter, use
-/// `{counter(page).at(loc)}` instead.
-///
-/// - returns: integer
-///
-/// ### position()
-/// Returns a dictionary with the page number and the x, y position for this
-/// location. The page number starts at one and the coordinates are measured
-/// from the top-left of the page.
-///
-/// If you only need the page number, use `page()` instead as it allows Typst
-/// to skip unnecessary work.
-///
-/// - returns: dictionary
-///
-/// ### page-numbering()
-/// Returns the page numbering pattern of the page at this location. This can be
-/// used when displaying the page counter in order to obtain the local numbering.
-/// This is useful if you are building custom indices or outlines.
-///
-/// If the page numbering is set to `none` at that location, this function returns `none`.
-///
-/// - returns: string or function or none
-///
-/// Display: Locate
-/// Category: meta
#[func]
pub fn locate(
- /// A function that receives a `location`. Its return value is displayed
- /// in the document.
+ /// A function that receives a [`location`]($location). Its return value is
+ /// displayed in the document.
///
/// This function is called once for each time the content returned by
/// `locate` appears in the document. That makes it possible to generate
@@ -60,10 +26,7 @@ pub fn locate(
}
/// Executes a `locate` call.
-///
-/// Display: Locate
-/// Category: special
-#[element(Locatable, Show)]
+#[elem(Locatable, Show)]
struct LocateElem {
/// The function to call with the location.
#[required]
@@ -83,9 +46,9 @@ impl Show for LocateElem {
/// Provides access to active styles.
///
/// The styles are currently opaque and only useful in combination with the
-/// [`measure`]($func/measure) function. See its documentation for more details.
-/// In the future, the provided styles might also be directly accessed to look
-/// up styles defined by [set rules]($styling/#set-rules).
+/// [`measure`]($measure) function. See its documentation for more details. In
+/// the future, the provided styles might also be directly accessed to look up
+/// styles defined by [set rules]($styling/#set-rules).
///
/// ```example
/// #let thing(body) = style(styles => {
@@ -96,9 +59,6 @@ impl Show for LocateElem {
/// #thing[Hey] \
/// #thing[Welcome]
/// ```
-///
-/// Display: Style
-/// Category: meta
#[func]
pub fn style(
/// A function to call with the styles. Its return value is displayed
@@ -113,10 +73,7 @@ pub fn style(
}
/// Executes a style access.
-///
-/// Display: Style
-/// Category: special
-#[element(Show)]
+#[elem(Show)]
struct StyleElem {
/// The function to call with the styles.
#[required]
@@ -134,10 +91,8 @@ impl Show for StyleElem {
/// (width and height).
///
/// The given function must accept a single parameter, `size`, which is a
-/// dictionary with keys `width` and `height`, both of type
-/// [`length`]($type/length).
+/// dictionary with keys `width` and `height`, both of type [`length`]($length).
///
-
/// ```example
/// #let text = lorem(30)
/// #layout(size => style(styles => [
@@ -155,9 +110,9 @@ impl Show for StyleElem {
/// and a height of `{400pt}`, then the specified function will be given the
/// parameter `{(width: 800pt, height: 400pt)}`. If it placed directly into the
/// page it receives the page's dimensions minus its margins. This is mostly
-/// useful in combination with [measurement]($func/measure).
+/// useful in combination with [measurement]($measure).
///
-/// You can also use this function to resolve [`ratio`]($type/ratio) to fixed
+/// You can also use this function to resolve [`ratio`]($ratio) to fixed
/// lengths. This might come in handy if you're building your own layout
/// abstractions.
///
@@ -170,16 +125,13 @@ impl Show for StyleElem {
///
/// Note that this function will provide an infinite width or height if one of
/// the page width or height is `auto`, respectively.
-///
-/// Display: Layout
-/// Category: meta
#[func]
pub fn layout(
/// A function to call with the outer container's size. Its return value is
/// displayed in the document.
///
- /// The container's size is given as a [dictionary]($type/dictionary) with
- /// the keys `width` and `height`.
+ /// The container's size is given as a [dictionary]($dictionary) with the
+ /// keys `width` and `height`.
///
/// This function is called once for each time the content returned by
/// `layout` appears in the document. That makes it possible to generate
@@ -190,10 +142,7 @@ pub fn layout(
}
/// Executes a `layout` call.
-///
-/// Display: Layout
-/// Category: special
-#[element(Layout)]
+#[elem(Layout)]
struct LayoutElem {
/// The function to call with the outer container's (or page's) size.
#[required]
diff --git a/crates/typst-library/src/meta/counter.rs b/crates/typst-library/src/meta/counter.rs
index 6c437469..a2a63e81 100644
--- a/crates/typst-library/src/meta/counter.rs
+++ b/crates/typst-library/src/meta/counter.rs
@@ -17,15 +17,14 @@ use crate::prelude::*;
/// headings, figures, and more. Moreover, you can define custom counters for
/// other things you want to count.
///
-/// ## Displaying a counter { #displaying }
+/// # Displaying a counter { #displaying }
/// To display the current value of the heading counter, you call the `counter`
/// function with the `key` set to `heading` and then call the `display` method
/// on the counter. To see any output, you also have to enable heading
-/// [numbering]($func/heading.numbering).
+/// [numbering]($heading.numbering).
///
/// The `display` method optionally takes an argument telling it how to format
-/// the counter. This can be a [numbering pattern or a
-/// function]($func/numbering).
+/// the counter. This can be a [numbering pattern or a function]($numbering).
///
/// ```example
/// #set heading(numbering: "1.")
@@ -41,7 +40,7 @@ use crate::prelude::*;
/// #counter(heading).display("I")
/// ```
///
-/// ## Modifying a counter { #modifying }
+/// # Modifying a counter { #modifying }
/// To modify a counter, you can use the `step` and `update` methods:
///
/// - The `step` method increases the value of the counter by one. Because
@@ -76,7 +75,6 @@ use crate::prelude::*;
/// Still at #counter(heading).display().
/// ```
///
-/// ## Custom counters { #custom-counters }
/// To define your own counter, call the `counter` function with a string as a
/// key. This key identifies the counter globally.
///
@@ -89,7 +87,7 @@ use crate::prelude::*;
/// #mine.display() \
/// ```
///
-/// ## How to step { #how-to-step }
+/// # How to step
/// When you define and use a custom counter, in general, you should first step
/// the counter and then display it. This way, the stepping behaviour of a
/// counter can depend on the element it is stepped for. If you were writing a
@@ -118,7 +116,7 @@ use crate::prelude::*;
/// they always start at zero. This way, they are at one for the first display
/// (which happens after the first step).
///
-/// ## Page counter { #page-counter }
+/// # Page counter
/// The page counter is special. It is automatically stepped at each pagebreak.
/// But like other counters, you can also step it manually. For example, you
/// could have Roman page numbers for your preface, then switch to Arabic page
@@ -145,7 +143,7 @@ use crate::prelude::*;
/// Arabic numbers.
/// ```
///
-/// ## Time travel { #time-travel }
+/// # Time travel
/// Counters can travel through time! You can find out the final value of the
/// counter before it is reached and even determine what the value was at any
/// particular location in the document.
@@ -177,16 +175,16 @@ use crate::prelude::*;
///
/// Let's dissect what happens in the example above:
///
-/// - We call [`locate`]($func/locate) to get access to the current location in
-/// the document. We then pass this location to our counter's `at` method to
-/// get its value at the current location. The `at` method always returns an
-/// array because counters can have multiple levels. As the counter starts at
-/// zero, the first value is thus `{(0,)}`.
+/// - We call [`locate`]($locate) to get access to the current location in the
+/// document. We then pass this location to our counter's `at` method to get
+/// its value at the current location. The `at` method always returns an array
+/// because counters can have multiple levels. As the counter starts at zero,
+/// the first value is thus `{(0,)}`.
///
-/// - We now [`query`]($func/query) the document for all elements with the
+/// - We now [`query`]($query) the document for all elements with the
/// `{<intro>}` label. The result is an array from which we extract the first
-/// (and only) element's [location]($type/content.location). We then look up
-/// the value of the counter at that location. The first update to the counter
+/// (and only) element's [location]($content.location). We then look up the
+/// value of the counter at that location. The first update to the counter
/// sets it to `{0 + 3 = 3}`. At the introduction heading, the value is thus
/// `{(3,)}`.
///
@@ -196,180 +194,31 @@ use crate::prelude::*;
/// which one doesn't matter. After the heading follow two calls to `step()`,
/// so the final value is `{(5,)}`.
///
-/// ## Other kinds of state { #other-state }
-/// The `counter` function is closely related to [state]($func/state) function.
-/// Read its documentation for more details on state management in Typst and
-/// why it doesn't just use normal variables for counters.
-///
-/// ## Methods
-/// ### display()
-/// Displays the value of the counter.
-///
-/// - numbering: string or function (positional)
-/// A [numbering pattern or a function]($func/numbering), which specifies how
-/// to display the counter. If given a function, that function receives each
-/// number of the counter as a separate argument. If the amount of numbers
-/// varies, e.g. for the heading argument, you can use an
-/// [argument sink]($type/arguments).
-///
-/// If this is omitted, displays the counter with the numbering style for the
-/// counted element or with the pattern `{"1.1"}` if no such style exists.
-///
-/// - both: boolean (named)
-/// If enabled, displays the current and final top-level count together. Both
-/// can be styled through a single numbering pattern. This is used by the page
-/// numbering property to display the current and total number of pages when a
-/// pattern like `{"1 / 1"}` is given.
-///
-/// - returns: content
-///
-/// ### step()
-/// Increases the value of the counter by one.
-///
-/// The update will be in effect at the position where the returned content is
-/// inserted into the document. If you don't put the output into the document,
-/// nothing happens! This would be the case, for example, if you write
-/// `{let _ = counter(page).step()}`. Counter updates are always applied in
-/// layout order and in that case, Typst wouldn't know when to step the counter.
-///
-/// - level: integer (named)
-/// The depth at which to step the counter. Defaults to `{1}`.
-///
-/// - returns: content
-///
-/// ### update()
-/// Updates the value of the counter.
-///
-/// Just like with `step`, the update only occurs if you put the resulting
-/// content into the document.
-///
-/// - value: integer or array or function (positional, required)
-/// If given an integer or array of integers, sets the counter to that value.
-/// If given a function, that function receives the previous counter value
-/// (with each number as a separate argument) and has to return the new
-/// value (integer or array).
-///
-/// - returns: content
-///
-/// ### at()
-/// Gets the value of the counter at the given location. Always returns an
-/// array of integers, even if the counter has just one number.
-///
-/// - location: location (positional, required)
-/// The location at which the counter value should be retrieved. A suitable
-/// location can be retrieved from [`locate`]($func/locate) or
-/// [`query`]($func/query).
-///
-/// - returns: array
-///
-/// ### final()
-/// Gets the value of the counter at the end of the document. Always returns an
-/// array of integers, even if the counter has just one number.
-///
-/// - location: location (positional, required)
-/// Can be an arbitrary location, as its value is irrelevant for the method's
-/// return value. Why is it required then? Typst has to evaluate parts of your
-/// code multiple times to determine all counter values. By only allowing this
-/// method within [`locate`]($func/locate) calls, the amount of code that can
-/// depend on the method's result is reduced. If you could call `final`
-/// directly at the top level of a module, the evaluation of the whole module
-/// and its exports could depend on the counter's value.
-///
-/// - returns: array
-///
-/// Display: Counter
-/// Category: meta
-#[func]
-pub fn counter(
- /// The key that identifies this counter.
- ///
- /// - If it is a string, creates a custom counter that is only affected by
- /// manual updates,
- /// - If this is a `{<label>}`, counts through all elements with that label,
- /// - If this is an element function or selector, counts through its elements,
- /// - If this is the [`page`]($func/page) function, counts through pages.
- key: CounterKey,
-) -> Counter {
- Counter::new(key)
-}
-
-/// Counts through pages, elements, and more.
+/// # Other kinds of state { #other-state }
+/// The `counter` type is closely related to [state]($state) type. Read its
+/// documentation for more details on state management in Typst and why it
+/// doesn't just use normal variables for counters.
+#[ty(scope)]
#[derive(Clone, PartialEq, Hash)]
pub struct Counter(CounterKey);
impl Counter {
- /// Create a new counter from a key.
- pub fn new(key: CounterKey) -> Self {
+ /// Create a new counter identified by a key.
+ pub fn new(key: CounterKey) -> Counter {
Self(key)
}
/// The counter for the given element.
- pub fn of(func: ElemFunc) -> Self {
- Self::new(CounterKey::Selector(Selector::Elem(func, None)))
- }
-
- /// Call a method on counter.
- #[tracing::instrument(skip(vm))]
- pub fn call_method(
- self,
- vm: &mut Vm,
- method: &str,
- mut args: Args,
- span: Span,
- ) -> SourceResult<Value> {
- let value = match method {
- "display" => self
- .display(args.eat()?, args.named("both")?.unwrap_or(false))
- .into_value(),
- "step" => self
- .update(CounterUpdate::Step(
- args.named("level")?.unwrap_or(NonZeroUsize::ONE),
- ))
- .into_value(),
- "update" => self.update(args.expect("value or function")?).into_value(),
- "at" => self.at(&mut vm.vt, args.expect("location")?)?.into_value(),
- "final" => self.final_(&mut vm.vt, args.expect("location")?)?.into_value(),
- _ => bail!(span, "type counter has no method `{}`", method),
- };
- args.finish()?;
- Ok(value)
- }
-
- /// Display the current value of the counter.
- pub fn display(self, numbering: Option<Numbering>, both: bool) -> Content {
- DisplayElem::new(self, numbering, both).pack()
- }
-
- /// Get the value of the state at the given location.
- pub fn at(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
- let sequence = self.sequence(vt)?;
- let offset = vt.introspector.query(&self.selector().before(location, true)).len();
- let (mut state, page) = sequence[offset].clone();
- if self.is_page() {
- let delta = vt.introspector.page(location).get().saturating_sub(page.get());
- state.step(NonZeroUsize::ONE, delta);
- }
-
- Ok(state)
- }
-
- /// Get the value of the state at the final location.
- pub fn final_(&self, vt: &mut Vt, _: Location) -> SourceResult<CounterState> {
- let sequence = self.sequence(vt)?;
- let (mut state, page) = sequence.last().unwrap().clone();
- if self.is_page() {
- let delta = vt.introspector.pages().get().saturating_sub(page.get());
- state.step(NonZeroUsize::ONE, delta);
- }
- Ok(state)
+ pub fn of(func: Element) -> Self {
+ Self::construct(CounterKey::Selector(Selector::Elem(func, None)))
}
- /// Get the current and final value of the state combined in one state.
+ /// Gets the current and final value of the state combined in one state.
pub fn both(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
let offset = vt
.introspector
- .query(&Selector::before(self.selector(), location, true))
+ .query(&self.selector().before(location.into(), true))
.len();
let (mut at_state, at_page) = sequence[offset].clone();
let (mut final_state, final_page) = sequence.last().unwrap().clone();
@@ -384,12 +233,7 @@ impl Counter {
Ok(CounterState(smallvec![at_state.first(), final_state.first()]))
}
- /// Produce content that performs a state update.
- pub fn update(self, update: CounterUpdate) -> Content {
- UpdateElem::new(self.0, update).pack()
- }
-
- /// Produce the whole sequence of counter states.
+ /// Produces the whole sequence of counter states.
///
/// This has to happen just once for all counters, cutting down the number
/// of counter updates from quadratic to linear.
@@ -462,7 +306,7 @@ impl Counter {
/// The selector relevant for this counter's updates.
fn selector(&self) -> Selector {
let mut selector =
- Selector::Elem(UpdateElem::func(), Some(dict! { "key" => self.0.clone() }));
+ Selector::Elem(UpdateElem::elem(), Some(dict! { "key" => self.0.clone() }));
if let CounterKey::Selector(key) = &self.0 {
selector = Selector::Or(eco_vec![selector, key.clone()]);
@@ -477,6 +321,140 @@ impl Counter {
}
}
+#[scope]
+impl Counter {
+ /// Create a new counter identified by a key.
+ #[func(constructor)]
+ pub fn construct(
+ /// The key that identifies this counter.
+ ///
+ /// - If it is a string, creates a custom counter that is only affected
+ /// by manual updates,
+ /// - If this is a `{<label>}`, counts through all elements with that
+ /// label,
+ /// - If this is an element function or selector, counts through its
+ /// elements,
+ /// - If this is the [`page`]($page) function, counts through pages.
+ key: CounterKey,
+ ) -> Counter {
+ Self(key)
+ }
+
+ /// Displays the current value of the counter.
+ #[func]
+ pub fn display(
+ self,
+ /// A [numbering pattern or a function]($numbering), which specifies how
+ /// to display the counter. If given a function, that function receives
+ /// each number of the counter as a separate argument. If the amount of
+ /// numbers varies, e.g. for the heading argument, you can use an
+ /// [argument sink]($arguments).
+ ///
+ /// If this is omitted, displays the counter with the numbering style
+ /// for the counted element or with the pattern `{"1.1"}` if no such
+ /// style exists.
+ #[default]
+ numbering: Option<Numbering>,
+ /// If enabled, displays the current and final top-level count together.
+ /// Both can be styled through a single numbering pattern. This is used
+ /// by the page numbering property to display the current and total
+ /// number of pages when a pattern like `{"1 / 1"}` is given.
+ #[named]
+ #[default(false)]
+ both: bool,
+ ) -> Content {
+ DisplayElem::new(self, numbering, both).pack()
+ }
+
+ /// Increases the value of the counter by one.
+ ///
+ /// The update will be in effect at the position where the returned content
+ /// is inserted into the document. If you don't put the output into the
+ /// document, nothing happens! This would be the case, for example, if you
+ /// write `{let _ = counter(page).step()}`. Counter updates are always
+ /// applied in layout order and in that case, Typst wouldn't know when to
+ /// step the counter.
+ #[func]
+ pub fn step(
+ self,
+ /// The depth at which to step the counter. Defaults to `{1}`.
+ #[named]
+ #[default(NonZeroUsize::ONE)]
+ level: NonZeroUsize,
+ ) -> Content {
+ self.update(CounterUpdate::Step(level))
+ }
+
+ /// Updates the value of the counter.
+ ///
+ /// Just like with `step`, the update only occurs if you put the resulting
+ /// content into the document.
+ #[func]
+ pub fn update(
+ self,
+ /// If given an integer or array of integers, sets the counter to that
+ /// value. If given a function, that function receives the previous
+ /// counter value (with each number as a separate argument) and has to
+ /// return the new value (integer or array).
+ update: CounterUpdate,
+ ) -> Content {
+ UpdateElem::new(self.0, update).pack()
+ }
+
+ /// Gets the value of the counter at the given location. Always returns an
+ /// array of integers, even if the counter has just one number.
+ #[func]
+ pub fn at(
+ &self,
+ /// The virtual typesetter.
+ vt: &mut Vt,
+ /// The location at which the counter value should be retrieved. A
+ /// suitable location can be retrieved from [`locate`]($locate) or
+ /// [`query`]($query).
+ location: Location,
+ ) -> SourceResult<CounterState> {
+ let sequence = self.sequence(vt)?;
+ let offset = vt
+ .introspector
+ .query(&self.selector().before(location.into(), true))
+ .len();
+ let (mut state, page) = sequence[offset].clone();
+ if self.is_page() {
+ let delta = vt.introspector.page(location).get().saturating_sub(page.get());
+ state.step(NonZeroUsize::ONE, delta);
+ }
+
+ Ok(state)
+ }
+
+ /// Gets the value of the counter at the end of the document. Always returns
+ /// an array of integers, even if the counter has just one number.
+ #[func]
+ pub fn final_(
+ &self,
+ /// The virtual typesetter.
+ vt: &mut Vt,
+ /// Can be an arbitrary location, as its value is irrelevant for the
+ /// method's return value. Why is it required then? Typst has to
+ /// evaluate parts of your code multiple times to determine all counter
+ /// values. By only allowing this method within [`locate`]($locate)
+ /// calls, the amount of code that can depend on the method's result is
+ /// reduced. If you could call `final` directly at the top level of a
+ /// module, the evaluation of the whole module and its exports could
+ /// depend on the counter's value.
+ location: Location,
+ ) -> SourceResult<CounterState> {
+ let _ = location;
+ let sequence = self.sequence(vt)?;
+ let (mut state, page) = sequence.last().unwrap().clone();
+ if self.is_page() {
+ let delta = vt.introspector.pages().get().saturating_sub(page.get());
+ state.step(NonZeroUsize::ONE, delta);
+ }
+ Ok(state)
+ }
+}
+
impl Debug for Counter {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("counter(")?;
@@ -486,7 +464,7 @@ impl Debug for Counter {
}
cast! {
- type Counter: "counter",
+ type Counter,
}
/// Identifies a counter.
@@ -504,14 +482,14 @@ pub enum CounterKey {
cast! {
CounterKey,
self => match self {
- Self::Page => PageElem::func().into_value(),
+ Self::Page => PageElem::elem().into_value(),
Self::Selector(v) => v.into_value(),
Self::Str(v) => v.into_value(),
},
v: Str => Self::Str(v),
v: Label => Self::Selector(Selector::Label(v)),
- v: ElemFunc => {
- if v == PageElem::func() {
+ v: Element => {
+ if v == PageElem::elem() {
Self::Page
} else {
Self::Selector(LocatableSelector::from_value(v.into_value())?.0)
@@ -531,6 +509,7 @@ impl Debug for CounterKey {
}
/// An update to perform on a counter.
+#[ty]
#[derive(Clone, PartialEq, Hash)]
pub enum CounterUpdate {
/// Set the counter to the specified state.
@@ -548,7 +527,7 @@ impl Debug for CounterUpdate {
}
cast! {
- type CounterUpdate: "counter update",
+ type CounterUpdate,
v: CounterState => Self::Set(v),
v: Func => Self::Func(v),
}
@@ -612,10 +591,7 @@ cast! {
}
/// Executes a display of a state.
-///
-/// Display: State
-/// Category: special
-#[element(Locatable, Show)]
+#[elem(Locatable, Show)]
struct DisplayElem {
/// The counter.
#[required]
@@ -643,11 +619,11 @@ impl Show for DisplayElem {
return None;
};
- if func == HeadingElem::func() {
+ if func == HeadingElem::elem() {
HeadingElem::numbering_in(styles)
- } else if func == FigureElem::func() {
+ } else if func == FigureElem::elem() {
FigureElem::numbering_in(styles)
- } else if func == EquationElem::func() {
+ } else if func == EquationElem::elem() {
EquationElem::numbering_in(styles)
} else {
None
@@ -667,10 +643,7 @@ impl Show for DisplayElem {
}
/// Executes a display of a state.
-///
-/// Display: State
-/// Category: special
-#[element(Locatable, Show)]
+#[elem(Locatable, Show)]
struct UpdateElem {
/// The key that identifies the counter.
#[required]
diff --git a/crates/typst-library/src/meta/document.rs b/crates/typst-library/src/meta/document.rs
index db036e0a..66f8aeb5 100644
--- a/crates/typst-library/src/meta/document.rs
+++ b/crates/typst-library/src/meta/document.rs
@@ -17,10 +17,7 @@ use crate::prelude::*;
///
/// Note that metadata set with this function is not rendered within the
/// document. Instead, it is embedded in the compiled PDF file.
-///
-/// Display: Document
-/// Category: meta
-#[element(Construct, LayoutRoot)]
+#[elem(Construct, LayoutRoot)]
pub struct DocumentElem {
/// The document's title. This is often rendered as the title of the
/// PDF viewer window.
diff --git a/crates/typst-library/src/meta/figure.rs b/crates/typst-library/src/meta/figure.rs
index d63ae3a8..6e95dce7 100644
--- a/crates/typst-library/src/meta/figure.rs
+++ b/crates/typst-library/src/meta/figure.rs
@@ -15,7 +15,7 @@ use crate::visualize::ImageElem;
/// For example, figures containing images will be numbered separately from
/// figures containing tables.
///
-/// ## Examples { #examples }
+/// # Examples
/// The example below shows a basic figure with an image:
/// ```example
/// @glacier shows a glacier. Glaciers
@@ -27,9 +27,8 @@ use crate::visualize::ImageElem;
/// ) <glacier>
/// ```
///
-/// You can also insert [tables]($func/table) into figures to give them a
-/// caption. The figure will detect this and automatically use a separate
-/// counter.
+/// You can also insert [tables]($table) into figures to give them a caption.
+/// The figure will detect this and automatically use a separate counter.
///
/// ```example
/// #figure(
@@ -45,7 +44,7 @@ use crate::visualize::ImageElem;
/// This behaviour can be overridden by explicitly specifying the figure's
/// `kind`. All figures of the same kind share a common counter.
///
-/// ## Modifying the appearance { #modifying-appearance }
+/// # Modifying the appearance { #modifying-appearance }
/// You can completely customize the look of your figures with a [show
/// rule]($styling/#show-rules). In the example below, we show the figure's
/// caption above its body and display its supplement and counter after the
@@ -73,13 +72,10 @@ use crate::visualize::ImageElem;
/// If your figure is too large and its contents are breakable across pages
/// (e.g. if it contains a large table), then you can make the figure breakable
/// across pages as well by using `[#show figure: set block(breakable: true)]`
-/// (see the [block]($func/block) documentation for more information).
-///
-/// Display: Figure
-/// Category: meta
-#[element(Locatable, Synthesize, Count, Show, Finalize, Refable, Outlinable)]
+/// (see the [block]($block) documentation for more information).
+#[elem(Locatable, Synthesize, Count, Show, Finalize, Refable, Outlinable)]
pub struct FigureElem {
- /// The content of the figure. Often, an [image]($func/image).
+ /// The content of the figure. Often, an [image]($image).
#[required]
pub body: Content,
@@ -103,7 +99,7 @@ pub struct FigureElem {
/// )
/// #lorem(60)
/// ```
- pub placement: Option<Smart<VerticalAlign>>,
+ pub placement: Option<Smart<VAlign>>,
/// The figure's caption.
pub caption: Option<Content>,
@@ -122,8 +118,17 @@ pub struct FigureElem {
/// caption: [I'm down here],
/// )
/// ```
- #[default(VerticalAlign(GenAlign::Specific(Align::Bottom)))]
- pub caption_pos: VerticalAlign,
+ #[default(VAlign::Bottom)]
+ #[parse({
+ let option: Option<Spanned<VAlign>> = args.named("caption-pos")?;
+ if let Some(Spanned { v: align, span }) = option {
+ if align == VAlign::Horizon {
+ bail!(span, "expected `top` or `bottom`");
+ }
+ }
+ option.map(|spanned| spanned.v)
+ })]
+ pub caption_pos: VAlign,
/// The kind of figure this is.
///
@@ -133,7 +138,7 @@ pub struct FigureElem {
/// Setting this to something other than `{auto}` will override the
/// automatic detection. This can be useful if
/// - you wish to create a custom figure type that is not an
- /// [image]($func/image), a [table]($func/table) or [code]($func/raw),
+ /// [image]($image), a [table]($table) or [code]($raw),
/// - you want to force the figure to use a specific counter regardless of
/// its content.
///
@@ -155,8 +160,8 @@ pub struct FigureElem {
/// The figure's supplement.
///
/// If set to `{auto}`, the figure will try to automatically determine the
- /// correct supplement based on the `kind` and the active [text
- /// language]($func/text.lang). If you are using a custom figure type, you
+ /// correct supplement based on the `kind` and the active
+ /// [text language]($text.lang). If you are using a custom figure type, you
/// will need to manually specify the supplement.
///
/// If a function is specified, it is passed the first descendant of the
@@ -174,7 +179,7 @@ pub struct FigureElem {
pub supplement: Smart<Option<Supplement>>,
/// How to number the figure. Accepts a
- /// [numbering pattern or function]($func/numbering).
+ /// [numbering pattern or function]($numbering).
#[default(Some(NumberingPattern::from_str("1").unwrap().into()))]
pub numbering: Option<Numbering>,
@@ -182,16 +187,15 @@ pub struct FigureElem {
#[default(Em::new(0.65).into())]
pub gap: Length,
- /// Whether the figure should appear in an [`outline`]($func/outline)
- /// of figures.
+ /// Whether the figure should appear in an [`outline`]($outline) of figures.
#[default(true)]
pub outlined: bool,
/// Convenience field to get access to the counter for this figure.
///
/// The counter only depends on the `kind`:
- /// - For (tables)[$func/table]: `{counter(figure.where(kind: table))}`
- /// - For (images)[$func/image]: `{counter(figure.where(kind: image))}`
+ /// - For (tables)[@table]: `{counter(figure.where(kind: table))}`
+ /// - For (images)[@image]: `{counter(figure.where(kind: image))}`
/// - For a custom kind: `{counter(figure.where(kind: kind))}`
///
/// These are the counters you'll need to modify if you want to skip a
@@ -210,16 +214,9 @@ impl Synthesize for FigureElem {
.query_first(Selector::can::<dyn Figurable>())
.cloned()
.map(|elem| FigureKind::Elem(elem.func()))
- .unwrap_or_else(|| FigureKind::Elem(ImageElem::func()))
+ .unwrap_or_else(|| FigureKind::Elem(ImageElem::elem()))
});
- let caption_pos =
- VerticalAlign(GenAlign::Specific(match self.caption_pos(styles) {
- VerticalAlign(GenAlign::Specific(Align::Top)) => Align::Top,
- VerticalAlign(GenAlign::Specific(Align::Bottom)) => Align::Bottom,
- _ => bail!(self.span(), "caption-pos can only be top or bottom"),
- }));
-
// Resolve the supplement.
let supplement = match self.supplement(styles) {
Smart::Auto => {
@@ -261,14 +258,14 @@ impl Synthesize for FigureElem {
// Construct the figure's counter.
let counter = Counter::new(CounterKey::Selector(Selector::Elem(
- Self::func(),
+ Self::elem(),
Some(dict! {
"kind" => kind.clone(),
}),
)));
self.push_placement(self.placement(styles));
- self.push_caption_pos(caption_pos);
+ self.push_caption_pos(self.caption_pos(styles));
self.push_caption(self.caption(styles));
self.push_kind(Smart::Custom(kind));
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
@@ -288,10 +285,7 @@ impl Show for FigureElem {
// Build the caption, if any.
if let Some(caption) = self.full_caption(vt)? {
let v = VElem::weak(self.gap(styles).into()).pack();
- realized = if matches!(
- self.caption_pos(styles),
- VerticalAlign(GenAlign::Specific(Align::Bottom))
- ) {
+ realized = if self.caption_pos(styles) == VAlign::Bottom {
realized + v + caption
} else {
caption + v + realized
@@ -302,15 +296,13 @@ impl Show for FigureElem {
realized = BlockElem::new()
.with_body(Some(realized))
.pack()
- .aligned(Axes::with_x(Some(Align::Center.into())));
+ .aligned(Align::CENTER);
// Wrap in a float.
if let Some(align) = self.placement(styles) {
realized = PlaceElem::new(realized)
.with_float(true)
- .with_alignment(align.map(|VerticalAlign(align)| {
- Axes::new(Some(Align::Center.into()), Some(align))
- }))
+ .with_alignment(align.map(|align| HAlign::Center + align))
.pack();
}
@@ -345,7 +337,7 @@ impl Refable for FigureElem {
}
fn counter(&self) -> Counter {
- self.counter().unwrap_or_else(|| Counter::of(Self::func()))
+ self.counter().unwrap_or_else(|| Counter::of(Self::elem()))
}
fn numbering(&self) -> Option<Numbering> {
@@ -379,8 +371,8 @@ impl FigureElem {
self.counter(),
self.numbering(StyleChain::default()),
) {
- let loc = self.0.location().unwrap();
- let numbers = counter.at(vt, loc)?.display(vt, &numbering)?;
+ let location = self.0.location().unwrap();
+ let numbers = counter.at(vt, location)?.display(vt, &numbering)?;
if !supplement.is_empty() {
supplement += TextElem::packed("\u{a0}");
@@ -397,7 +389,7 @@ impl FigureElem {
#[derive(Debug, Clone)]
pub enum FigureKind {
/// The kind is an element function.
- Elem(ElemFunc),
+ Elem(Element),
/// The kind is a name.
Name(EcoString),
}
@@ -408,7 +400,7 @@ cast! {
Self::Elem(v) => v.into_value(),
Self::Name(v) => v.into_value(),
},
- v: ElemFunc => Self::Elem(v),
+ v: Element => Self::Elem(v),
v: EcoString => Self::Name(v),
}
diff --git a/crates/typst-library/src/meta/footnote.rs b/crates/typst-library/src/meta/footnote.rs
index 848c0b7c..ed7242bb 100644
--- a/crates/typst-library/src/meta/footnote.rs
+++ b/crates/typst-library/src/meta/footnote.rs
@@ -34,11 +34,11 @@ cast! {
/// and can break across multiple pages.
///
/// To customize the appearance of the entry in the footnote listing, see
-/// [`footnote.entry`]($func/footnote.entry). The footnote itself is realized as
-/// a normal superscript, so you can use a set rule on the
-/// [`super`]($func/super) function to customize it.
+/// [`footnote.entry`]($footnote.entry). The footnote itself is realized as a
+/// normal superscript, so you can use a set rule on the [`super`]($super)
+/// function to customize it.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// Check the docs for more details.
/// #footnote[https://typst.app/docs]
@@ -46,7 +46,7 @@ cast! {
///
/// The footnote automatically attaches itself to the preceding word, even if
/// there is a space before it in the markup. To force space, you can use the
-/// string `[#" "]` or explicit [horizontal spacing]($func/h).
+/// string `[#" "]` or explicit [horizontal spacing]($h).
///
/// By giving a label to a footnote, you can have multiple references to it.
///
@@ -61,21 +61,14 @@ cast! {
/// apply to the footnote's content. See [here][issue] for more information.
///
/// [issue]: https://github.com/typst/typst/issues/1467#issuecomment-1588799440
-///
-/// Display: Footnote
-/// Category: meta
-#[element(Locatable, Synthesize, Show, Count)]
-#[scope(
- scope.define("entry", FootnoteEntry::func());
- scope
-)]
+#[elem(scope, Locatable, Synthesize, Show, Count)]
pub struct FootnoteElem {
/// How to number footnotes.
///
/// By default, the footnote numbering continues throughout your document.
/// If you prefer per-page footnote numbering, you can reset the footnote
- /// [counter]($func/counter) in the page [header]($func/page.header). In the
- /// future, there might be a simpler way to achieve this.
+ /// [counter]($counter) in the page [header]($page.header). In the future,
+ /// there might be a simpler way to achieve this.
///
/// ```example
/// #set footnote(numbering: "*")
@@ -93,6 +86,12 @@ pub struct FootnoteElem {
pub body: FootnoteBody,
}
+#[scope]
+impl FootnoteElem {
+ #[elem]
+ type FootnoteEntry;
+}
+
impl FootnoteElem {
/// Creates a new footnote that the passed content as its body.
pub fn with_content(content: Content) -> Self {
@@ -145,7 +144,7 @@ impl Show for FootnoteElem {
Ok(vt.delayed(|vt| {
let loc = self.declaration_location(vt).at(self.span())?;
let numbering = self.numbering(styles);
- let counter = Counter::of(Self::func());
+ let counter = Counter::of(Self::elem());
let num = counter.at(vt, loc)?.display(vt, &numbering)?;
let sup = SuperElem::new(num).pack();
let hole = HElem::new(Abs::zero().into()).with_weak(true).pack();
@@ -168,11 +167,9 @@ impl Count for FootnoteElem {
///
/// _Note:_ Set and show rules for `footnote.entry` must be defined at the
/// beginning of the document in order to work correctly.
-/// See [here][issue] for more information.
-///
-/// [issue]: https://github.com/typst/typst/issues/1348#issuecomment-1566316463
+/// See [here](https://github.com/typst/typst/issues/1348#issuecomment-1566316463)
+/// for more information.
///
-/// ## Example { #example }
/// ```example
/// #show footnote.entry: set text(red)
///
@@ -180,10 +177,7 @@ impl Count for FootnoteElem {
/// #footnote[It's down here]
/// has red text!
/// ```
-///
-/// Display: Footnote Entry
-/// Category: meta
-#[element(Show, Finalize)]
+#[elem(name = "entry", title = "Footnote Entry", Show, Finalize)]
pub struct FootnoteEntry {
/// The footnote for this entry. It's location can be used to determine
/// the footnote counter state.
@@ -220,7 +214,7 @@ pub struct FootnoteEntry {
#[default(
LineElem::new()
.with_length(Ratio::new(0.3).into())
- .with_stroke(PartialStroke {
+ .with_stroke(Stroke {
thickness: Smart::Custom(Abs::pt(0.5).into()),
..Default::default()
})
@@ -273,7 +267,7 @@ impl Show for FootnoteEntry {
let note = self.note();
let number_gap = Em::new(0.05);
let numbering = note.numbering(StyleChain::default());
- let counter = Counter::of(FootnoteElem::func());
+ let counter = Counter::of(FootnoteElem::elem());
let loc = note.0.location().unwrap();
let num = counter.at(vt, loc)?.display(vt, &numbering)?;
let sup = SuperElem::new(num)
diff --git a/crates/typst-library/src/meta/heading.rs b/crates/typst-library/src/meta/heading.rs
index 84da7c03..edd2d0f0 100644
--- a/crates/typst-library/src/meta/heading.rs
+++ b/crates/typst-library/src/meta/heading.rs
@@ -17,14 +17,13 @@ use crate::text::{SpaceElem, TextElem, TextSize};
///
/// Typst can automatically number your headings for you. To enable numbering,
/// specify how you want your headings to be numbered with a
-/// [numbering pattern or function]($func/numbering).
+/// [numbering pattern or function]($numbering).
///
/// Independently from the numbering, Typst can also automatically generate an
-/// [outline]($func/outline) of all headings for you. To exclude one or more
-/// headings from this outline, you can set the `outlined` parameter to
-/// `{false}`.
+/// [outline]($outline) of all headings for you. To exclude one or more headings
+/// from this outline, you can set the `outlined` parameter to `{false}`.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #set heading(numbering: "1.a)")
///
@@ -35,21 +34,18 @@ use crate::text::{SpaceElem, TextElem, TextSize};
/// To start, ...
/// ```
///
-/// ## Syntax { #syntax }
+/// # Syntax
/// Headings have dedicated syntax: They can be created by starting a line with
/// one or multiple equals signs, followed by a space. The number of equals
/// signs determines the heading's logical nesting depth.
-///
-/// Display: Heading
-/// Category: meta
-#[element(Locatable, Synthesize, Count, Show, Finalize, LocalName, Refable, Outlinable)]
+#[elem(Locatable, Synthesize, Count, Show, Finalize, LocalName, Refable, Outlinable)]
pub struct HeadingElem {
/// The logical nesting depth of the heading, starting from one.
#[default(NonZeroUsize::ONE)]
pub level: NonZeroUsize,
/// How to number the heading. Accepts a
- /// [numbering pattern or function]($func/numbering).
+ /// [numbering pattern or function]($numbering).
///
/// ```example
/// #set heading(numbering: "1.a.")
@@ -78,11 +74,11 @@ pub struct HeadingElem {
/// ```
pub supplement: Smart<Option<Supplement>>,
- /// Whether the heading should appear in the [outline]($func/outline).
+ /// Whether the heading should appear in the [outline]($outline).
///
- /// Note that this property, if set to `{true}`, ensures the heading is
- /// also shown as a bookmark in the exported PDF's outline (when exporting
- /// to PDF). To change that behavior, use the `bookmarked` property.
+ /// Note that this property, if set to `{true}`, ensures the heading is also
+ /// shown as a bookmark in the exported PDF's outline (when exporting to
+ /// PDF). To change that behavior, use the `bookmarked` property.
///
/// ```example
/// #outline()
@@ -103,9 +99,8 @@ pub struct HeadingElem {
/// The default value of `{auto}` indicates that the heading will only
/// appear in the exported PDF's outline if its `outlined` property is set
/// to `{true}`, that is, if it would also be listed in Typst's
- /// [outline]($func/outline). Setting this property to either
- /// `{true}` (bookmark) or `{false}` (don't bookmark) bypasses that
- /// behavior.
+ /// [outline]($outline). Setting this property to either `{true}` (bookmark)
+ /// or `{false}` (don't bookmark) bypasses that behavior.
///
/// ```example
/// #heading[Normal heading]
@@ -149,7 +144,7 @@ impl Show for HeadingElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body();
if let Some(numbering) = self.numbering(styles) {
- realized = Counter::of(Self::func())
+ realized = Counter::of(Self::elem())
.display(Some(numbering), false)
.spanned(self.span())
+ HElem::new(Em::new(0.3).into()).with_weak(true).pack()
@@ -205,7 +200,7 @@ impl Refable for HeadingElem {
}
fn counter(&self) -> Counter {
- Counter::of(Self::func())
+ Counter::of(Self::elem())
}
fn numbering(&self) -> Option<Numbering> {
@@ -221,7 +216,7 @@ impl Outlinable for HeadingElem {
let mut content = self.body();
if let Some(numbering) = self.numbering(StyleChain::default()) {
- let numbers = Counter::of(Self::func())
+ let numbers = Counter::of(Self::elem())
.at(vt, self.0.location().unwrap())?
.display(vt, &numbering)?;
content = numbers + SpaceElem::new().pack() + content;
diff --git a/crates/typst-library/src/meta/link.rs b/crates/typst-library/src/meta/link.rs
index 2a53b84f..7b68b186 100644
--- a/crates/typst-library/src/meta/link.rs
+++ b/crates/typst-library/src/meta/link.rs
@@ -6,7 +6,7 @@ use crate::text::{Hyphenate, TextElem};
/// By default, links are not styled any different from normal text. However,
/// you can easily apply a style of your choice with a show rule.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #show link: underline
///
@@ -18,13 +18,10 @@ use crate::text::{Hyphenate, TextElem};
/// ]
/// ```
///
-/// ## Syntax { #syntax }
+/// # Syntax
/// This function also has dedicated syntax: Text that starts with `http://` or
/// `https://` is automatically turned into a link.
-///
-/// Display: Link
-/// Category: meta
-#[element(Show)]
+#[elem(Show)]
pub struct LinkElem {
/// The destination the link points to.
///
@@ -35,17 +32,16 @@ pub struct LinkElem {
///
/// - To link to another part of the document, `dest` can take one of three
/// forms:
- /// - A [label]($func/label) attached to an element. If you also want
- /// automatic text for the link based on the element, consider using
- /// a [reference]($func/ref) instead.
+ /// - A [label]($label) attached to an element. If you also want automatic
+ /// text for the link based on the element, consider using a
+ /// [reference]($ref) instead.
///
- /// - A [location]($func/locate) resulting from a [`locate`]($func/locate)
- /// call or [`query`]($func/query).
+ /// - A [location]($locate) resulting from a [`locate`]($locate) call or
+ /// [`query`]($query).
///
- /// - A dictionary with a `page` key of type [integer]($type/integer) and
- /// `x` and `y` coordinates of type [length]($type/length). Pages are
- /// counted from one, and the coordinates are relative to the page's top
- /// left corner.
+ /// - A dictionary with a `page` key of type [integer]($int) and `x` and
+ /// `y` coordinates of type [length]($length). Pages are counted from
+ /// one, and the coordinates are relative to the page's top left corner.
///
/// ```example
/// = Introduction <intro>
diff --git a/crates/typst-library/src/meta/metadata.rs b/crates/typst-library/src/meta/metadata.rs
index 3e08a9f7..b4ae64cb 100644
--- a/crates/typst-library/src/meta/metadata.rs
+++ b/crates/typst-library/src/meta/metadata.rs
@@ -2,11 +2,11 @@ use crate::prelude::*;
/// Exposes a value to the query system without producing visible content.
///
-/// This element can be retrieved with the [`query`]($func/query) function and
-/// from the command with [`typst query`]($reference/meta/query/#cli-queries).
-/// Its purpose is to expose an arbitrary value to the introspection system. To
-/// identify a metadata value among others, you can attach a
-/// [`label`]($func/label) to it and query for that label.
+/// This element can be retrieved with the [`query`]($query) function and from
+/// the command with [`typst query`]($reference/meta/query/#cli-queries). Its
+/// purpose is to expose an arbitrary value to the introspection system. To
+/// identify a metadata value among others, you can attach a [`label`]($label)
+/// to it and query for that label.
///
/// The `metadata` element is especially useful for command line queries because
/// it allows you to expose arbitrary values to the outside world.
@@ -20,10 +20,7 @@ use crate::prelude::*;
/// query(<note>, loc).first().value
/// })
/// ```
-///
-/// Display: Metadata
-/// Category: meta
-#[element(Behave, Show, Locatable)]
+#[elem(Behave, Show, Locatable)]
pub struct MetadataElem {
/// The value to embed into the document.
#[required]
diff --git a/crates/typst-library/src/meta/mod.rs b/crates/typst-library/src/meta/mod.rs
index bb6ac3d3..659cb5a3 100644
--- a/crates/typst-library/src/meta/mod.rs
+++ b/crates/typst-library/src/meta/mod.rs
@@ -9,9 +9,11 @@ mod footnote;
mod heading;
mod link;
mod metadata;
-mod numbering;
+#[path = "numbering.rs"]
+mod numbering_;
mod outline;
-mod query;
+#[path = "query.rs"]
+mod query_;
mod reference;
mod state;
@@ -24,9 +26,9 @@ pub use self::footnote::*;
pub use self::heading::*;
pub use self::link::*;
pub use self::metadata::*;
-pub use self::numbering::*;
+pub use self::numbering_::*;
pub use self::outline::*;
-pub use self::query::*;
+pub use self::query_::*;
pub use self::reference::*;
pub use self::state::*;
@@ -35,24 +37,27 @@ use crate::text::TextElem;
/// Hook up all meta definitions.
pub(super) fn define(global: &mut Scope) {
- global.define("document", DocumentElem::func());
- global.define("ref", RefElem::func());
- global.define("link", LinkElem::func());
- global.define("outline", OutlineElem::func());
- global.define("heading", HeadingElem::func());
- global.define("figure", FigureElem::func());
- global.define("footnote", FootnoteElem::func());
- global.define("cite", CiteElem::func());
- global.define("bibliography", BibliographyElem::func());
- global.define("locate", locate_func());
- global.define("style", style_func());
- global.define("layout", layout_func());
- global.define("counter", counter_func());
- global.define("numbering", numbering_func());
- global.define("state", state_func());
- global.define("query", query_func());
- global.define("selector", selector_func());
- global.define("metadata", MetadataElem::func());
+ global.category("meta");
+ global.define_type::<Label>();
+ global.define_type::<Selector>();
+ global.define_type::<Location>();
+ global.define_type::<Counter>();
+ global.define_type::<State>();
+ global.define_elem::<DocumentElem>();
+ global.define_elem::<RefElem>();
+ global.define_elem::<LinkElem>();
+ global.define_elem::<OutlineElem>();
+ global.define_elem::<HeadingElem>();
+ global.define_elem::<FigureElem>();
+ global.define_elem::<FootnoteElem>();
+ global.define_elem::<CiteElem>();
+ global.define_elem::<BibliographyElem>();
+ global.define_elem::<MetadataElem>();
+ global.define_func::<locate>();
+ global.define_func::<style>();
+ global.define_func::<layout>();
+ global.define_func::<numbering>();
+ global.define_func::<query>();
}
/// The named with which an element is referenced.
diff --git a/crates/typst-library/src/meta/numbering.rs b/crates/typst-library/src/meta/numbering.rs
index 8698f7b9..40308af0 100644
--- a/crates/typst-library/src/meta/numbering.rs
+++ b/crates/typst-library/src/meta/numbering.rs
@@ -16,7 +16,7 @@ use crate::text::Case;
/// number is substituted, their prefixes, and one suffix. The prefixes and the
/// suffix are repeated as-is.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #numbering("1.1)", 1, 2, 3) \
/// #numbering("1.a.i", 1, 2) \
@@ -29,11 +29,10 @@ use crate::text::Case;
/// 1, 2, 3,
/// )
/// ```
-///
-/// Display: Numbering
-/// Category: meta
#[func]
pub fn numbering(
+ /// The virtual machine.
+ vm: &mut Vm,
/// Defines how the numbering works.
///
/// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`, `א`, `가`,
@@ -64,8 +63,6 @@ pub fn numbering(
/// given, the last counting symbol with its prefix is repeated.
#[variadic]
numbers: Vec<usize>,
- /// The virtual machine.
- vm: &mut Vm,
) -> SourceResult<Value> {
numbering.apply_vm(vm, &numbers)
}
diff --git a/crates/typst-library/src/meta/outline.rs b/crates/typst-library/src/meta/outline.rs
index 6fd4ebd0..8583d96f 100644
--- a/crates/typst-library/src/meta/outline.rs
+++ b/crates/typst-library/src/meta/outline.rs
@@ -16,7 +16,7 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
/// be displayed in the outline alongside its title or caption. By default this
/// generates a table of contents.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #outline()
///
@@ -27,13 +27,13 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
/// #lorem(10)
/// ```
///
-/// ## Alternative outlines { #alternative-outlines }
+/// # Alternative outlines
/// By setting the `target` parameter, the outline can be used to generate a
/// list of other kinds of elements than headings. In the example below, we list
/// all figures containing images by setting `target` to `{figure.where(kind:
/// image)}`. We could have also set it to just `figure`, but then the list
/// would also include figures containing tables or other material. For more
-/// details on the `where` selector, [see here]($type/content.where).
+/// details on the `where` selector, [see here]($function.where).
///
/// ```example
/// #outline(
@@ -47,25 +47,17 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
/// )
/// ```
///
-/// ## Styling the outline { #styling-the-outline }
+/// # Styling the outline
/// The outline element has several options for customization, such as its
-/// `title` and `indent` parameters. If desired, however, it is possible to
-/// have more control over the outline's look and style through the
-/// [`outline.entry`]($func/outline.entry) element.
-///
-/// Display: Outline
-/// Category: meta
-/// Keywords: Table of Contents
-#[element(Show, Finalize, LocalName)]
-#[scope(
- scope.define("entry", OutlineEntry::func());
- scope
-)]
+/// `title` and `indent` parameters. If desired, however, it is possible to have
+/// more control over the outline's look and style through the
+/// [`outline.entry`]($outline.entry) element.
+#[elem(scope, keywords = ["Table of Contents"], Show, Finalize, LocalName)]
pub struct OutlineElem {
/// The title of the outline.
///
/// - When set to `{auto}`, an appropriate title for the
- /// [text language]($func/text.lang) will be used. This is the default.
+ /// [text language]($text.lang) will be used. This is the default.
/// - When set to `{none}`, the outline will not have a title.
/// - A custom title can be set by passing content.
///
@@ -97,7 +89,7 @@ pub struct OutlineElem {
/// )
/// ```
#[default(LocatableSelector(Selector::Elem(
- HeadingElem::func(),
+ HeadingElem::elem(),
Some(dict! { "outlined" => true })
)))]
pub target: LocatableSelector,
@@ -125,19 +117,18 @@ pub struct OutlineElem {
/// - `{none}`: No indent
/// - `{auto}`: Indents the numbering of the nested entry with the title of
/// its parent entry. This only has an effect if the entries are numbered
- /// (e.g., via [heading numbering]($func/heading.numbering)).
- /// - [Relative length]($type/relative-length): Indents the item by this length
+ /// (e.g., via [heading numbering]($heading.numbering)).
+ /// - [Relative length]($relative): Indents the item by this length
/// multiplied by its nesting level. Specifying `{2em}`, for instance,
/// would indent top-level headings (not nested) by `{0em}`, second level
/// headings by `{2em}` (nested once), third-level headings by `{4em}`
/// (nested twice) and so on.
- /// - [Function]($type/function): You can completely customize this setting
- /// with a function. That function receives the nesting level as a
- /// parameter (starting at 0 for top-level headings/elements) and can
- /// return a relative length or content making up the indent. For example,
- /// `{n => n * 2em}` would be equivalent to just specifying `{2em}`,
- /// while `{n => [→ ] * n}` would indent with one arrow per nesting
- /// level.
+ /// - [Function]($function): You can completely customize this setting with
+ /// a function. That function receives the nesting level as a parameter
+ /// (starting at 0 for top-level headings/elements) and can return a
+ /// relative length or content making up the indent. For example,
+ /// `{n => n * 2em}` would be equivalent to just specifying `{2em}`, while
+ /// `{n => [→ ] * n}` would indent with one arrow per nesting level.
///
/// *Migration hints:* Specifying `{true}` (equivalent to `{auto}`) or
/// `{false}` (equivalent to `{none}`) for this option is deprecated and
@@ -184,6 +175,12 @@ pub struct OutlineElem {
pub fill: Option<Content>,
}
+#[scope]
+impl OutlineElem {
+ #[elem]
+ type OutlineEntry;
+}
+
impl Show for OutlineElem {
#[tracing::instrument(name = "OutlineElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
@@ -391,11 +388,9 @@ cast! {
/// outlined element, its page number, and the filler content between both.
///
/// This element is intended for use with show rules to control the appearance
-/// of outlines.
-///
-/// ## Example { #example }
-/// The example below shows how to style entries for top-level sections to make
-/// them stand out.
+/// of outlines. To customize an entry's line, you can build it from scratch by
+/// accessing the `level`, `element`, `body`, `fill` and `page` fields on the
+/// entry.
///
/// ```example
/// #set heading(numbering: "1.")
@@ -416,13 +411,7 @@ cast! {
/// = Analysis
/// == Setup
/// ```
-///
-/// To completely customize an entry's line, you can also build it from scratch
-/// by accessing the `level`, `element`, `body`, `fill` and `page` fields on the entry.
-///
-/// Display: Outline Entry
-/// Category: meta
-#[element(Show)]
+#[elem(name = "entry", title = "Outline Entry", Show)]
pub struct OutlineEntry {
/// The nesting level of this outline entry. Starts at `{1}` for top-level
/// entries.
@@ -430,8 +419,8 @@ pub struct OutlineEntry {
pub level: NonZeroUsize,
/// The element this entry refers to. Its location will be available
- /// through the [`location`]($type/content.location) method on content
- /// and can be [linked]($func/link) to.
+ /// through the [`location`]($content.location) method on content
+ /// and can be [linked]($link) to.
#[required]
pub element: Content,
@@ -446,7 +435,7 @@ pub struct OutlineEntry {
/// located in. When `{none}`, empty space is inserted in that gap instead.
///
/// Note that, when using show rules to override outline entries, it is
- /// recommended to wrap the filling content in a [`box`]($func/box) with
+ /// recommended to wrap the filling content in a [`box`]($box) with
/// fractional width. For example, `{box(width: 1fr, repeat[-])}` would show
/// precisely as many `-` characters as necessary to fill a particular gap.
#[required]
diff --git a/crates/typst-library/src/meta/query.rs b/crates/typst-library/src/meta/query.rs
index eb520896..d6c600d7 100644
--- a/crates/typst-library/src/meta/query.rs
+++ b/crates/typst-library/src/meta/query.rs
@@ -4,10 +4,10 @@ use crate::prelude::*;
///
/// The `query` functions lets you search your document for elements of a
/// particular type or with a particular label. To use it, you first need to
-/// retrieve the current document location with the [`locate`]($func/locate)
+/// retrieve the current document location with the [`locate`]($locate)
/// function.
///
-/// ## Finding elements { #finding-elements }
+/// # Finding elements
/// In the example below, we create a custom page header that displays the text
/// "Typst Academy" in small capitals and the current section title. On the
/// first page, the section title is omitted because the header is before the
@@ -59,7 +59,7 @@ use crate::prelude::*;
/// #lorem(15)
/// ```
///
-/// ## A word of caution { #caution }
+/// # A word of caution { #caution }
/// To resolve all your queries, Typst evaluates and layouts parts of the
/// document multiple times. However, there is no guarantee that your queries
/// can actually be completely resolved. If you aren't careful a query can
@@ -73,9 +73,9 @@ use crate::prelude::*;
/// on. As we can see, the output has five headings. This is because Typst
/// simply gives up after five attempts.
///
-/// In general, you should try not to write queries that affect themselves.
-/// The same words of caution also apply to other introspection features like
-/// [counters]($func/counter) and [state]($func/state).
+/// In general, you should try not to write queries that affect themselves. The
+/// same words of caution also apply to other introspection features like
+/// [counters]($counter) and [state]($state).
///
/// ```example
/// = Real
@@ -86,11 +86,11 @@ use crate::prelude::*;
/// })
/// ```
///
-/// ## Command line queries { #command-line-queries }
+/// # Command line queries
/// You can also perform queries from the command line with the `typst query`
/// command. This command executes an arbitrary query on the document and
/// returns the resulting elements in serialized form. Consider the following
-/// `example.typ` file which contains some invisible [metadata]($func/metadata):
+/// `example.typ` file which contains some invisible [metadata]($metadata):
///
/// ```typ
/// #metadata("This is a note") <note>
@@ -125,32 +125,29 @@ use crate::prelude::*;
/// $ typst query example.typ "<note>" --field value --one
/// "This is a note"
/// ```
-///
-/// Display: Query
-/// Category: meta
#[func]
pub fn query(
+ /// The virtual machine.
+ vm: &mut Vm,
/// Can be an element function like a `heading` or `figure`, a `{<label>}`
/// or a more complex selector like `{heading.where(level: 1)}`.
///
/// Currently, only a subset of element functions is supported. Aside from
/// headings and figures, this includes equations, references and all
/// elements with an explicit label. As a result, you _can_ query for e.g.
- /// [`strong`]($func/strong) elements, but you will find only those that
- /// have an explicit label attached to them. This limitation will be
- /// resolved in the future.
+ /// [`strong`]($strong) elements, but you will find only those that have an
+ /// explicit label attached to them. This limitation will be resolved in the
+ /// future.
target: LocatableSelector,
/// Can be an arbitrary location, as its value is irrelevant for the
/// function's return value. Why is it required then? As noted before, Typst
/// has to evaluate parts of your code multiple times to determine the
/// values of all state. By only allowing this function within
- /// [`locate`]($func/locate) calls, the amount of code that can depend on
- /// the query's result is reduced. If you could call it directly at the top
+ /// [`locate`]($locate) calls, the amount of code that can depend on the
+ /// query's result is reduced. If you could call it directly at the top
/// level of a module, the evaluation of the whole module and its exports
/// could depend on the query's result.
location: Location,
- /// The virtual machine.
- vm: &mut Vm,
) -> Array {
let _ = location;
let vec = vm.vt.introspector.query(&target.0);
@@ -158,19 +155,3 @@ pub fn query(
.map(|elem| Value::Content(elem.into_inner()))
.collect()
}
-
-/// Turns a value into a selector. The following values are accepted:
-/// - An element function like a `heading` or `figure`.
-/// - A `{<label>}`.
-/// - A more complex selector like `{heading.where(level: 1)}`.
-///
-/// Display: Selector
-/// Category: meta
-#[func]
-pub fn selector(
- /// Can be an element function like a `heading` or `figure`, a `{<label>}`
- /// or a more complex selector like `{heading.where(level: 1)}`.
- target: Selector,
-) -> Selector {
- target
-}
diff --git a/crates/typst-library/src/meta/reference.rs b/crates/typst-library/src/meta/reference.rs
index 015157a8..7f05bfcc 100644
--- a/crates/typst-library/src/meta/reference.rs
+++ b/crates/typst-library/src/meta/reference.rs
@@ -9,20 +9,19 @@ use crate::text::TextElem;
/// Produces a textual reference to a label. For example, a reference to a
/// heading will yield an appropriate string such as "Section 1" for a reference
/// to the first heading. The references are also links to the respective
-/// element. Reference syntax can also be used to [cite]($func/cite) from a
+/// element. Reference syntax can also be used to [cite]($cite) from a
/// bibliography.
///
-/// Referenceable elements include [headings]($func/heading),
-/// [figures]($func/figure), [equations]($func/math.equation), and
-/// [footnotes]($func/footnote). To create a custom referenceable element like a
-/// theorem, you can create a figure of a custom [`kind`]($func/figure.kind) and
-/// write a show rule for it. In the future, there might be a more direct way to
-/// define a custom referenceable element.
+/// Referenceable elements include [headings]($heading), [figures]($figure),
+/// [equations]($math.equation), and [footnotes]($footnote). To create a custom
+/// referenceable element like a theorem, you can create a figure of a custom
+/// [`kind`]($figure.kind) and write a show rule for it. In the future, there
+/// might be a more direct way to define a custom referenceable element.
///
/// If you just want to link to a labelled element and not get an automatic
-/// textual reference, consider using the [`link`]($func/link) function instead.
+/// textual reference, consider using the [`link`]($link) function instead.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #set heading(numbering: "1.")
/// #set math.equation(numbering: "(1)")
@@ -46,7 +45,7 @@ use crate::text::TextElem;
/// #bibliography("works.bib")
/// ```
///
-/// ## Syntax { #syntax }
+/// # Syntax
/// This function also has dedicated syntax: A reference to a label can be
/// created by typing an `@` followed by the name of the label (e.g.
/// `[= Introduction <intro>]` can be referenced by typing `[@intro]`).
@@ -54,7 +53,7 @@ use crate::text::TextElem;
/// To customize the supplement, add content in square brackets after the
/// reference: `[@intro[Chapter]]`.
///
-/// ## Customization { #customization }
+/// # Customization
/// If you write a show rule for references, you can access the referenced
/// element through the `element` field of the reference. The `element` may
/// be `{none}` even if it exists if Typst hasn't discovered it yet, so you
@@ -83,10 +82,7 @@ use crate::text::TextElem;
/// In @beginning we prove @pythagoras.
/// $ a^2 + b^2 = c^2 $ <pythagoras>
/// ```
-///
-/// Display: Reference
-/// Category: meta
-#[element(Synthesize, Locatable, Show)]
+#[elem(title = "Reference", Synthesize, Locatable, Show)]
pub struct RefElem {
/// The target label that should be referenced.
#[required]
@@ -163,7 +159,7 @@ impl Show for RefElem {
let elem = elem.at(span)?;
- if elem.func() == FootnoteElem::func() {
+ if elem.func() == FootnoteElem::elem() {
return Ok(FootnoteElem::with_label(target).pack().spanned(span));
}
@@ -192,7 +188,7 @@ impl Show for RefElem {
.hint(eco_format!(
"you can enable {} numbering with `#set {}(numbering: \"1.\")`",
elem.func().name(),
- if elem.func() == EquationElem::func() {
+ if elem.func() == EquationElem::elem() {
"math.equation"
} else {
elem.func().name()
diff --git a/crates/typst-library/src/meta/state.rs b/crates/typst-library/src/meta/state.rs
index ee2a6e32..f7b5eb77 100644
--- a/crates/typst-library/src/meta/state.rs
+++ b/crates/typst-library/src/meta/state.rs
@@ -30,7 +30,7 @@ use crate::prelude::*;
/// #compute("x - 5")
/// ```
///
-/// ## State and document markup { #state-and-markup }
+/// # State and document markup { #state-and-markup }
/// Why does it do that? Because, in general, this kind of computation with side
/// effects is problematic in document markup and Typst is upfront about that.
/// For the results to make sense, the computation must proceed in the same
@@ -61,7 +61,7 @@ use crate::prelude::*;
/// `template` function and only then sees the `Outline`. Just counting up would
/// number the `Introduction` with `1` and the `Outline` with `2`.
///
-/// ## Managing state in Typst { #state-in-typst }
+/// # Managing state in Typst { #state-in-typst }
/// So what do we do instead? We use Typst's state management system. Calling
/// the `state` function with an identifying string key and an optional initial
/// value gives you a state value which exposes a few methods. The two most
@@ -122,14 +122,14 @@ use crate::prelude::*;
///
/// This example is of course a bit silly, but in practice this is often exactly
/// what you want! A good example are heading counters, which is why Typst's
-/// [counting system]($func/counter) is very similar to its state system.
+/// [counting system]($counter) is very similar to its state system.
///
-/// ## Time Travel { #time-travel }
+/// # Time Travel
/// By using Typst's state management system you also get time travel
-/// capabilities! By combining the state system with [`locate`]($func/locate)
-/// and [`query`]($func/query), we can find out what the value of the state will
-/// be at any position in the document from anywhere else. In particular, the
-/// `at` method gives us the value of the state at any location and the `final`
+/// capabilities! By combining the state system with [`locate`]($locate) and
+/// [`query`]($query), we can find out what the value of the state will be at
+/// any position in the document from anywhere else. In particular, the `at`
+/// method gives us the value of the state at any location and the `final`
/// methods gives us the value of the state at the end of the document.
///
/// ```example
@@ -156,7 +156,7 @@ use crate::prelude::*;
/// #compute("x - 5")
/// ```
///
-/// ## A word of caution { #caution }
+/// # A word of caution { #caution }
/// To resolve the values of all states, Typst evaluates parts of your code
/// multiple times. However, there is no guarantee that your state manipulation
/// can actually be completely resolved.
@@ -180,73 +180,7 @@ use crate::prelude::*;
/// `locate` calls or `display` calls of state or counters. Instead, pass a
/// function to `update` that determines the value of the state based on its
/// previous value.
-///
-/// ## Methods
-/// ### display()
-/// Displays the value of the state.
-///
-/// - format: function (positional)
-/// A function which receives the value of the state and can return arbitrary
-/// content which is then displayed. If this is omitted, the value is directly
-/// displayed.
-///
-/// - returns: content
-///
-/// ### update()
-/// Updates the value of the state.
-///
-/// The update will be in effect at the position where the returned content is
-/// inserted into the document. If you don't put the output into the document,
-/// nothing happens! This would be the case, for example, if you write
-/// `{let _ = state("key").update(7)}`. State updates are always applied in
-/// layout order and in that case, Typst wouldn't know when to update the state.
-///
-/// - value: any or function (positional, required)
-/// If given a non function-value, sets the state to that value. If given a
-/// function, that function receives the previous state and has to return the
-/// new state.
-///
-/// - returns: content
-///
-/// ### at()
-/// Gets the value of the state at the given location.
-///
-/// - location: location (positional, required)
-/// The location at which the state's value should be retrieved. A suitable
-/// location can be retrieved from [`locate`]($func/locate) or
-/// [`query`]($func/query).
-///
-/// - returns: any
-///
-/// ### final()
-/// Gets the value of the state at the end of the document.
-///
-/// - location: location (positional, required)
-/// Can be an arbitrary location, as its value is irrelevant for the method's
-/// return value. Why is it required then? As noted before, Typst has to
-/// evaluate parts of your code multiple times to determine the values of all
-/// state. By only allowing this method within [`locate`]($func/locate) calls,
-/// the amount of code that can depend on the method's result is reduced. If
-/// you could call `final` directly at the top level of a module, the
-/// evaluation of the whole module and its exports could depend on the state's
-/// value.
-///
-/// - returns: any
-///
-/// Display: State
-/// Category: meta
-#[func]
-pub fn state(
- /// The key that identifies this state.
- key: Str,
- /// The initial value of the state.
- #[default]
- init: Value,
-) -> State {
- State { key, init }
-}
-
-/// A state.
+#[ty(scope)]
#[derive(Clone, PartialEq, Hash)]
pub struct State {
/// The key that identifies the state.
@@ -256,49 +190,9 @@ pub struct State {
}
impl State {
- /// Call a method on a state.
- #[tracing::instrument(skip(vm))]
- pub fn call_method(
- self,
- vm: &mut Vm,
- method: &str,
- mut args: Args,
- span: Span,
- ) -> SourceResult<Value> {
- let value = match method {
- "display" => self.display(args.eat()?).into_value(),
- "at" => self.at(&mut vm.vt, args.expect("location")?)?,
- "final" => self.final_(&mut vm.vt, args.expect("location")?)?,
- "update" => self.update(args.expect("value or function")?).into_value(),
- _ => bail!(span, "type state has no method `{}`", method),
- };
- args.finish()?;
- Ok(value)
- }
-
- /// Display the current value of the state.
- pub fn display(self, func: Option<Func>) -> Content {
- DisplayElem::new(self, func).pack()
- }
-
- /// Get the value of the state at the given location.
- #[tracing::instrument(skip(self, vt))]
- pub fn at(self, vt: &mut Vt, location: Location) -> SourceResult<Value> {
- let sequence = self.sequence(vt)?;
- let offset = vt.introspector.query(&self.selector().before(location, true)).len();
- Ok(sequence[offset].clone())
- }
-
- /// Get the value of the state at the final location.
- #[tracing::instrument(skip(self, vt))]
- pub fn final_(self, vt: &mut Vt, _: Location) -> SourceResult<Value> {
- let sequence = self.sequence(vt)?;
- Ok(sequence.last().unwrap().clone())
- }
-
- /// Produce content that performs a state update.
- pub fn update(self, update: StateUpdate) -> Content {
- UpdateElem::new(self.key, update).pack()
+ /// Create a new state identified by a key.
+ pub fn new(key: Str, init: Value) -> State {
+ Self { key, init }
}
/// Produce the whole sequence of states.
@@ -350,7 +244,94 @@ impl State {
/// The selector for this state's updates.
fn selector(&self) -> Selector {
- Selector::Elem(UpdateElem::func(), Some(dict! { "key" => self.key.clone() }))
+ Selector::Elem(UpdateElem::elem(), Some(dict! { "key" => self.key.clone() }))
+ }
+}
+
+#[scope]
+impl State {
+ /// Create a new state identified by a key.
+ #[func(constructor)]
+ pub fn construct(
+ /// The key that identifies this state.
+ key: Str,
+ /// The initial value of the state.
+ #[default]
+ init: Value,
+ ) -> State {
+ Self::new(key, init)
+ }
+
+ /// Displays the current value of the state.
+ #[func]
+ pub fn display(
+ self,
+ /// A function which receives the value of the state and can return
+ /// arbitrary content which is then displayed. If this is omitted, the
+ /// value is directly displayed.
+ #[default]
+ func: Option<Func>,
+ ) -> Content {
+ DisplayElem::new(self, func).pack()
+ }
+
+ /// Update the value of the state.
+ ///
+ /// The update will be in effect at the position where the returned content
+ /// is inserted into the document. If you don't put the output into the
+ /// document, nothing happens! This would be the case, for example, if you
+ /// write `{let _ = state("key").update(7)}`. State updates are always
+ /// applied in layout order and in that case, Typst wouldn't know when to
+ /// update the state.
+ #[func]
+ pub fn update(
+ self,
+ /// If given a non function-value, sets the state to that value. If
+ /// given a function, that function receives the previous state and has
+ /// to return the new state.
+ update: StateUpdate,
+ ) -> Content {
+ UpdateElem::new(self.key, update).pack()
+ }
+
+ /// Get the value of the state at the given location.
+ #[func]
+ pub fn at(
+ &self,
+ /// The virtual typesetter.
+ vt: &mut Vt,
+ /// The location at which the state's value should be retrieved. A
+ /// suitable location can be retrieved from [`locate`]($locate) or
+ /// [`query`]($query).
+ location: Location,
+ ) -> SourceResult<Value> {
+ let sequence = self.sequence(vt)?;
+ let offset = vt
+ .introspector
+ .query(&self.selector().before(location.into(), true))
+ .len();
+ Ok(sequence[offset].clone())
+ }
+
+ /// Get the value of the state at the end of the document.
+ #[func]
+ pub fn final_(
+ &self,
+ /// The virtual typesetter.
+ vt: &mut Vt,
+ /// Can be an arbitrary location, as its value is irrelevant for the
+ /// method's return value. Why is it required then? As noted before,
+ /// Typst has to evaluate parts of your code multiple times to determine
+ /// the values of all state. By only allowing this method within
+ /// [`locate`]($locate) calls, the amount of code that can depend on the
+ /// method's result is reduced. If you could call `final` directly at
+ /// the top level of a module, the evaluation of the whole module and
+ /// its exports could depend on the state's value.
+ location: Location,
+ ) -> SourceResult<Value> {
+ let _ = location;
+ let sequence = self.sequence(vt)?;
+ Ok(sequence.last().unwrap().clone())
}
}
@@ -365,10 +346,11 @@ impl Debug for State {
}
cast! {
- type State: "state",
+ type State,
}
/// An update to perform on a state.
+#[ty]
#[derive(Clone, PartialEq, Hash)]
pub enum StateUpdate {
/// Set the state to the specified value.
@@ -384,16 +366,13 @@ impl Debug for StateUpdate {
}
cast! {
- type StateUpdate: "state update",
+ type StateUpdate,
v: Func => Self::Func(v),
v: Value => Self::Set(v),
}
/// Executes a display of a state.
-///
-/// Display: State
-/// Category: special
-#[element(Locatable, Show)]
+#[elem(Locatable, Show)]
struct DisplayElem {
/// The state.
#[required]
@@ -419,10 +398,7 @@ impl Show for DisplayElem {
}
/// Executes a display of a state.
-///
-/// Display: State
-/// Category: special
-#[element(Locatable, Show)]
+#[elem(Locatable, Show)]
struct UpdateElem {
/// The key that identifies the state.
#[required]