Repository: typst/typst
File: crates/typst-library/src/introspection/state.rs (Rust)

Code before:
        self.at_loc(engine, loc)
    }

    /// Retrieves the value of the state at the end of the document.
    #[func(contextual)]
    pub fn final_(
        &self,
        engine: &mut Engine,
        context: Tracked<Context>,
        span: Span,
    ) -> SourceResult<Value> {
        context.introspect().at(span)?;
        let sequence = self.sequence(engine)?;
        Ok(sequence.last().unwrap().clone())
    }

    /// Updates the value of the state.
    ///
    /// Returns an _invisible_ piece of content that doesn't affect document
    /// layout (similar to [`metadata`]). 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 will happen! 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.
    ///
    /// In contrast to [`get`]($state.get), [`at`]($state.at), and
    /// [`final`]($state.final), this function does not require [context].
    #[func]
    pub fn update(
        self,
        span: Span,
        /// A value to update to or a function to update with.
        ///
        /// - If given a non-function value, sets the state to that value.
        /// - If given a function, that function receives the state's previous
        ///   value and has to return the state's new value.
        ///
        /// When updating the state based on its previous value, you should
        /// prefer the function form instead of retrieving the previous value
        /// from the [context]($context). This allows the compiler to resolve
        /// the final state efficiently, minimizing the number of
        /// [layout iterations]($context/#compiler-iterations) required.
        ///
        /// In the following example, `{fill.update(f => not f)}` will paint odd
        /// [items in the bullet list]($list.item) as expected. However, if it's
        /// replaced with `{context fill.update(not fill.get())}`, then layout
        /// will not converge within 5 attempts, as each update will take one
        /// additional iteration to propagate.
        ///

Review comment (suggestion):
If we would adjust this doc I think we should remove the old "Typst wouldn't know when to update the state" and replace it with an explanation that updates only happen if they are associated with a location in the document - only state updates that are in the document, update state. How perfectly tautological...

Code after:
        self.at_loc(engine, loc)
    }

    /// Retrieves the value of the state at the end of the document.
    #[func(contextual)]
    pub fn final_(
        &self,
        engine: &mut Engine,
        context: Tracked<Context>,
        span: Span,
    ) -> SourceResult<Value> {
        context.introspect().at(span)?;
        let sequence = self.sequence(engine)?;
        Ok(sequence.last().unwrap().clone())
    }

    /// Updates the value of the state.
    ///
    /// Returns an invisible piece of [content] that must be inserted into the
    /// document to take effect. This invisible content tells Typst that the
    /// specified update should take place wherever the content is inserted into
    /// the document.
    ///
    /// State is a part of your document and runs like a thread embedded in the
    /// document content. The value of a state is the result of all state
    /// updates that happened in the document up until that point.
    ///
    /// That's why `state.update` returns an invisible sliver of content that
    /// you need to return and include in the document — a state update that is
    /// not "placed" in the document does not happen, and "when" it happens is
    /// determined by where you place it. That's also why you need [context] to
    /// read state: You need to use the current document position to know where
    /// on the state's "thread" you are.
    ///
    /// Storing a state update in a variable (e.g.
    /// `{let my-update = state("key").update(c => c * 2)}`) will have no effect
    /// by itself. Only once you insert the variable `[#my-update]` somewhere
    /// into the document content, the update will take effect — at the position
    /// where it was inserted. You can also use `[#my-update]` multiple times at
    /// different positions. Then, the update will take effect multiple times as
    /// well.
    ///
    /// In contrast to [`get`]($state.get), [`at`]($state.at), and
    /// [`final`]($state.final), this function does not require [context]. This
    /// is because, to create the state update, we do not need to know where in
    /// the document we are. We only need this information to resolve the
    /// state's value.
    #[func]
    pub fn update(
        self,
        span: Span,