Document escaping semicolon, valid identifiers, and state tips (#6674)

Co-authored-by: Andrew Voynov <37143421+Andrew15-5@users.noreply.github.com>
Co-authored-by: Yaksher <yaksher.git@gmail.com>
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
Y.D.X. 2025-08-07 23:02:43 +08:00 committed by GitHub
parent 3455ac7dd2
commit 0b639c7510
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 9 deletions

View File

@ -75,7 +75,10 @@ impl Label {
/// Creates a label from a string. /// Creates a label from a string.
#[func(constructor)] #[func(constructor)]
pub fn construct( pub fn construct(
/// The name of the label. Must not be empty. /// The name of the label.
///
/// Unlike the [dedicated syntax](#syntax), this constructor accepts
/// any non-empty string, including names with special characters.
name: Str, name: Str,
) -> StrResult<Label> { ) -> StrResult<Label> {
if name.is_empty() { if name.is_empty() {

View File

@ -407,7 +407,7 @@ impl Counter {
/// Create a new counter identified by a key. /// Create a new counter identified by a key.
#[func(constructor)] #[func(constructor)]
pub fn construct( pub fn construct(
/// The key that identifies this counter. /// The key that identifies this counter globally.
/// ///
/// - If it is a string, creates a custom counter that is only affected /// - If it is a string, creates a custom counter that is only affected
/// by manual updates, /// by manual updates,

View File

@ -273,8 +273,31 @@ impl State {
#[func(constructor)] #[func(constructor)]
pub fn construct( pub fn construct(
/// The key that identifies this state. /// The key that identifies this state.
///
/// Any [updates]($state.update) to the state will be identified with
/// the string key. If you construct multiple states with the same
/// `key`, then updating any one will affect all of them.
key: Str, key: Str,
/// The initial value of the state. /// The initial value of the state.
///
/// If you construct multiple states with the same `key` but different
/// `init` values, they will each use their own initial value but share
/// updates. Specifically, the value of a state at some location in the
/// document will be computed from that state's initial value and all
/// preceding updates for the state's key.
///
/// ```example
/// #let banana = state("key", "🍌")
/// #let broccoli = state("key", "🥦")
///
/// #banana.update(it => it + "😋")
///
/// #context [
/// - #state("key", "🍎").get()
/// - #banana.get()
/// - #broccoli.get()
/// ]
/// ```
#[default] #[default]
init: Value, init: Value,
) -> State { ) -> State {
@ -328,7 +351,7 @@ impl State {
Ok(sequence.last().unwrap().clone()) Ok(sequence.last().unwrap().clone())
} }
/// Update the value of the state. /// Updates the value of the state.
/// ///
/// The update will be in effect at the position where the returned content /// 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 /// is inserted into the document. If you don't put the output into the
@ -336,13 +359,44 @@ impl State {
/// write `{let _ = state("key").update(7)}`. State updates are always /// write `{let _ = state("key").update(7)}`. State updates are always
/// applied in layout order and in that case, Typst wouldn't know when to /// applied in layout order and in that case, Typst wouldn't know when to
/// update the state. /// update the state.
///
/// In contrast to [`get`]($state.get), [`at`]($state.at), and
/// [`final`]($state.final), this function does not require [context].
#[func] #[func]
pub fn update( pub fn update(
self, self,
span: Span, span: Span,
/// If given a non function-value, sets the state to that value. If /// A value to update to or a function to update with.
/// given a function, that function receives the previous state and has ///
/// to return the new state. /// - 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.
///
/// ```example
/// #let fill = state("fill", false)
///
/// #show list.item: it => {
/// fill.update(f => not f)
/// context {
/// set text(fill: fuchsia) if fill.get()
/// it
/// }
/// }
///
/// #lorem(5).split().map(list.item).join()
/// ```
update: StateUpdate, update: StateUpdate,
) -> Content { ) -> Content {
StateUpdateElem::new(self.key, update).pack().spanned(span) StateUpdateElem::new(self.key, update).pack().spanned(span)

View File

@ -14,7 +14,7 @@ provides compact syntax to embed a code expression into markup: An expression is
introduced with a hash (`#`) and normal markup parsing resumes after the introduced with a hash (`#`) and normal markup parsing resumes after the
expression is finished. If a character would continue the expression but should expression is finished. If a character would continue the expression but should
be interpreted as text, the expression can forcibly be ended with a semicolon be interpreted as text, the expression can forcibly be ended with a semicolon
(`;`). (`;`). You can [escape a literal `#` or `;` with a backslash]($syntax/#escapes).
```example ```example
#emph[Hello] \ #emph[Hello] \
@ -66,6 +66,7 @@ Content and code blocks can be nested arbitrarily. In the example below,
## Bindings and Destructuring { #bindings } ## Bindings and Destructuring { #bindings }
As already demonstrated above, variables can be defined with `{let}` bindings. As already demonstrated above, variables can be defined with `{let}` bindings.
The variable is assigned the value of the expression that follows the `=` sign. The variable is assigned the value of the expression that follows the `=` sign.
A [valid variable name](#identifiers) may contain `-`, but cannot start with `-`.
The assignment of a value is optional, if no value is assigned, the variable The assignment of a value is optional, if no value is assigned, the variable
will be initialized as `{none}`. The `{let}` keyword can also be used to create will be initialized as `{none}`. The `{let}` keyword can also be used to create
a [custom named function]($function/#defining-functions). Variables can be a [custom named function]($function/#defining-functions). Variables can be
@ -77,8 +78,8 @@ is no containing block).
This is #name's documentation. This is #name's documentation.
It explains #name. It explains #name.
#let add(x, y) = x + y #let my-add(x, y) = x + y
Sum is #add(2, 3). Sum is #my-add(2, 3).
``` ```
Let bindings can also be used to destructure [arrays]($array) and Let bindings can also be used to destructure [arrays]($array) and

View File

@ -168,6 +168,34 @@ I got an ice cream for
\$1.50! \u{1f600} \$1.50! \u{1f600}
``` ```
## Identifiers
Names of variables, functions, and so on (_identifiers_) can contain letters,
numbers, hyphens (`-`), and underscores (`_`). They must start with a letter or
an underscore.
More specifically, the identifier syntax in Typst is based on the
[Unicode Standard Annex #31](https://www.unicode.org/reports/tr31/), with two
extensions: Allowing `_` as a starting character, and allowing both `_` and `-`
as continuing characters.
For multi-word identifiers, the recommended case convention is
[Kebab case](https://en.wikipedia.org/wiki/Letter_case#Kebab_case). In Kebab
case, words are written in lowercase and separated by hyphens (as in
`top-edge`). This is especially relevant when developing modules and packages
for others to use, as it keeps things predictable.
```example
#let kebab-case = [Using hyphen]
#let _schön = "😊"
#let 始料不及 = "😱"
#let π = calc.pi
#kebab-case
#if< 0 { _schön } else { 始料不及 }
// -π means -1 * π,
// so it's not a valid identifier
```
## Paths ## Paths
Typst has various features that require a file path to reference external Typst has various features that require a file path to reference external
resources such as images, Typst files, or data files. Paths are represented as resources such as images, Typst files, or data files. Paths are represented as