Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 70 additions & 85 deletions crates/typst-library/src/introspection/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ use crate::introspection::{Introspector, Locatable, Location};
use crate::routines::Routines;
use crate::World;

/// Manages stateful parts of your document.
/// 文書中の状態の管理。
///
/// Let's say you have some computations in your document and want to remember
/// the result of your last computation to use it in the next one. You might try
/// something similar to the code below and expect it to output 10, 13, 26, and
/// 21. However this **does not work** in Typst. If you test this code, you will
/// see that Typst complains with the following error message: _Variables from
/// outside the function are read-only and cannot be modified._
/// 文書中で何回か計算を行い、最後の計算結果を次の計算で使用するために記憶しておきたいとします。
/// 以下と同等のコードを試すと10、13、26、21と出力されることを期待するでしょう。
/// しかしTypstでは**そうはなりません**。
/// このコードを試してみると、Typstは _Variables from outside the function are read-only and cannot be modified._ というエラーメッセージを出力することが分かります。
///
/// ```typ
/// // This doesn't work!
Expand All @@ -38,17 +36,18 @@ use crate::World;
/// #compute("x - 5")
/// ```
///
/// # 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
/// order in which the results will be laid out in the document. In our simple
/// example, that's the case, but in general it might not be.
/// # 状態と文書のマークアップ { #state-and-markup }
/// なぜこうなるのでしょうか?
/// 一般的に副作用を伴うこの手の計算は文書のマークアップにおいて問題を引き起こすためで、Typstではこれをエラーとして扱います。
/// この結果を理解するには、計算処理が文書内で生成物がレイアウトされる順序と同じ順序で行われる必要があります。
/// 今回の単純な例ではこの条件が満たされますが、一般的には必ずしもそうとは限りません。
///
/// Let's look at a slightly different, but similar kind of state: The heading
/// numbering. We want to increase the heading counter at each heading. Easy
/// enough, right? Just add one. Well, it's not that simple. Consider the
/// following example:
/// 見出しの番号付けという、類似した状態ですが、少し異なる例を見てみましょう。
/// 各見出しで見出しカウンターの値を増やしたいとします。
/// 簡単ですよね?
/// ただ1を足すだけです。
/// 残念ながらそう単純ではないのです。
/// 以下の例を考えます。
///
/// ```example
/// #set heading(numbering: "1.")
Expand All @@ -64,28 +63,26 @@ use crate::World;
/// ...
/// ```
///
/// Here, Typst first processes the body of the document after the show rule,
/// sees the `Introduction` heading, then passes the resulting content to the
/// `template` function and only then sees the `Outline`. Just counting up would
/// number the `Introduction` with `1` and the `Outline` with `2`.
/// ここで、Typstはまずshowルール以降の文書本体を処理し、`Introduction`見出しを検知します。
/// 続いて`template`関数に生成コンテンツを渡します。
/// その後、初めて`Outline`を検知します。
/// 単にカウンター値を増やすと`Introduction``1`に、`Outline``2`となります。
///
/// # 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 functions. The two most
/// important ones are `get` and `update`:
/// # Typstにおける状態管理 { #state-in-typst }
/// それでは代わりにどうするのでしょうか?
/// Typstの状態管理システムを使用します。
/// 識別用のキーとなる文字列とオプションの初期値とともに`state`関数を呼び出すことで状態値が得られます。
/// この状態値はいくつかの関数を公開しており、最も重要な2つの関数が`get``update`です。
///
/// - The [`get`]($state.get) function retrieves the current value of the state.
/// Because the value can vary over the course of the document, it is a
/// _contextual_ function that can only be used when [context]($context) is
/// available.
/// - [`get`]($state.get)関数は状態の現在値を取得します。
/// 値は文書中で変化するため、これは[コンテキスト]($context)が利用可能な場合にのみ使用できる _コンテキスト_ 関数です。
///
/// - The [`update`]($state.update) function modifies the state. You can give it
/// any value. If given a non-function value, it sets the state to that value.
/// If given a function, that function receives the previous state and has to
/// return the new state.
/// - [`update`]($state.update)関数は状態に修正を加えます。
/// 任意の値が使用できます。
/// 関数ではない値が渡された場合、状態にその値が設定されます。
/// 関数が与えられた場合、その関数は前の状態を受け取り、新しい状態を返さなければなりません。
///
/// Our initial example would now look like this:
/// 最初の例は以下のようになります。
///
/// ```example
/// #let s = state("x", 0)
Expand All @@ -102,12 +99,10 @@ use crate::World;
/// #compute("x - 5")
/// ```
///
/// State managed by Typst is always updated in layout order, not in evaluation
/// order. The `update` method returns content and its effect occurs at the
/// position where the returned content is inserted into the document.
/// Typstが管理する状態は常に評価順ではなくレイアウト順で更新されます。
/// `update`メソッドはコンテンツを返し、その影響は文書に返されたコンテンツが挿入された場所で生じます。
///
/// As a result, we can now also store some of the computations in variables,
/// but they still show the correct results:
/// このようにして、計算結果を変数に保存できるようになり、正しい結果を表示しています。
///
/// ```example
/// >>> #let s = state("x", 0)
Expand All @@ -129,16 +124,14 @@ use crate::World;
/// #more
/// ```
///
/// 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]($counter) is very similar to its state system.
/// この例はもちろん少々極端ですが、これが実際に本当に必要となることがよくあります!
/// 良い例は見出しカウンターです。
/// これはTypstの[カウンターシステム]($counter)が状態システムにとてもよく似ているためです。
///
/// # Time Travel
/// By using Typst's state management system you also get time travel
/// capabilities! 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 particular location and the `final`
/// methods gives us the value of the state at the end of the document.
/// # タイムトラベル
/// Typstの状態管理システムを使用するとタイムトラベルもできます!
/// 文書内の任意の位置でその状態がどの値になっているのかを、どこからでも突き止めることができます。
/// 特に、`at`メソッドを用いると特定の任意の位置での状態値が取得でき、`final`メソッドを用いると文書の終わりでの状態値を取得できます。
///
/// ```example
/// >>> #let s = state("x", 0)
Expand All @@ -160,17 +153,15 @@ use crate::World;
/// #compute("x - 5")
/// ```
///
/// # 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.
/// # 注意事項 { #caution }
/// 全ての状態値を解決するために、Typstはコードを複数回評価します。
/// しかしながら、実際に状態操作が完全に解決されるかは保証されません。
///
/// For instance, if you generate state updates depending on the final value of
/// a state, the results might never converge. The example below illustrates
/// this. We initialize our state with `1` and then update it to its own final
/// value plus 1. So it should be `2`, but then its final value is `2`, so it
/// should be `3`, and so on. This example displays a finite value because Typst
/// simply gives up after a few attempts.
/// 例えば、状態の最終的な値に依存する更新を行う状態を作成した場合、決して収束しなくなるでしょう。
/// 以下の例はこの実演です。
/// 状態を`1`で初期化し、続いて自身の最終値に1を足した値に更新します。
/// したがって値は`2`になるべきですが、最終値が`2`となったので`3`に更新します。以下同様です。
/// この例では有限値が表示されていますが、これは単にTypstが数回試行した後に諦めるためです。
///
/// ```example
/// // This is bad!
Expand All @@ -179,11 +170,9 @@ use crate::World;
/// #context s.get()
/// ```
///
/// In general, you should try not to generate state updates from within context
/// expressions. If possible, try to express your updates as non-contextual
/// values or functions that compute the new value from the previous value.
/// Sometimes, it cannot be helped, but in those cases it is up to you to ensure
/// that the result converges.
/// 一般に、コンテキスト内部で更新を行う状態を作成しないようにしてください。
/// 可能であれば、更新内容をコンテキストに依存しない値として、あるいは前の値から新しい値を計算する関数として定義してください。
/// どうしても避けられない場合がありますが、その場合は結果が適切に収束することを保証することはあなたの責任です。
#[ty(scope)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct State {
Expand Down Expand Up @@ -270,21 +259,21 @@ impl State {

#[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)
}

/// Retrieves the value of the state at the current location.
/// 現在位置での状態値を取得。
///
/// This is equivalent to `{state.at(here())}`.
/// これは`{state.at(here())}`と等価です。
#[typst_macros::time(name = "state.get", span = span)]
#[func(contextual)]
pub fn get(
Expand All @@ -297,26 +286,25 @@ impl State {
self.at_loc(engine, loc)
}

/// Retrieves the value of the state at the given selector's unique match.
/// 指定したセレクターで一意に特定される対象の状態値を取得。
///
/// The `selector` must match exactly one element in the document. The most
/// useful kinds of selectors for this are [labels]($label) and
/// [locations]($location).
/// `selector`は文書中で厳密に1つだけの要素にマッチしなければなりません。
/// この目的で最も便利なセレクターは[ラベル]($label)と[位置]($location)です。
Copy link
Preview

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The translation of 'location' as '位置' (position) may be inconsistent. Consider using 'ロケーション' to maintain consistency with technical terminology, or verify that '位置' is the established translation throughout the codebase.

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// この目的で最も便利なセレクターは[ラベル]($label)と[位置]($location)です。
/// この目的で最も便利なセレクターは[ラベル]($label)と[ロケーション]($location)です。

#[typst_macros::time(name = "state.at", span = span)]
#[func(contextual)]
pub fn at(
&self,
engine: &mut Engine,
context: Tracked<Context>,
span: Span,
/// The place at which the state's value should be retrieved.
/// 状態値を取得する場所。
selector: LocatableSelector,
) -> SourceResult<Value> {
let loc = selector.resolve_unique(engine.introspector, context).at(span)?;
self.at_loc(engine, loc)
}

/// Retrieves the value of the state at the end of the document.
/// 文書の終わりでの状態値の取得。
#[func(contextual)]
pub fn final_(
&self,
Expand All @@ -329,21 +317,18 @@ impl State {
Ok(sequence.last().unwrap().clone())
}

/// 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.
/// 更新は、返り値であるコンテンツが文書中に挿入された位置で適用されます。
/// 文書中に出力がなければ何も起こりません!
/// 例えば`{let _ = state("key").update(7)}`と書いた場合が、この何も起きないときに該当します。
/// 状態の更新は常にレイアウト順に適用されるため、この場合にはTypstはいつ状態を更新するのか分かりません。
#[func]
pub fn update(
self,
span: Span,
/// 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 {
StateUpdateElem::new(self.key, update).pack().spanned(span)
Expand Down
2 changes: 1 addition & 1 deletion website/translation-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
"/docs/reference/introspection/location/": "untranslated",
"/docs/reference/introspection/metadata/": "untranslated",
"/docs/reference/introspection/query/": "untranslated",
"/docs/reference/introspection/state/": "untranslated",
"/docs/reference/introspection/state/": "translated",
"/docs/reference/data-loading/": "translated",
"/docs/reference/data-loading/cbor/": "translated",
"/docs/reference/data-loading/csv/": "translated",
Expand Down