Revised context.md according to github discussion

This commit is contained in:
Ullrich Koethe 2025-03-11 19:05:15 +01:00 committed by GitHub
parent c472ee382e
commit a9a8426d2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -28,25 +28,25 @@ outline, for instance, also provide the proper context to resolve counters.
## Behavior of the context keyword ## Behavior of the context keyword
Style properties frequently change within a document, for example by applying set Style properties frequently change within a document, for example by applying set
rules. To retrieve such poperties in a consistent way, one must first specify rules. To retrieve such poperties in a consistent way, one must first specify
the context where the query is to be executed. This is the purpose of the the precise context where the property should be retrieved. This can be achieved
`context` keyword. Once the context has been fixed, the property information with the `context` keyword. Once the context has been fixed, the property
is available through a simple field access syntax. For example, `text.lang` information is available through standard field access syntax. For example,
asks for the current language setting. In its simplest form, the `context` `text.lang` asks for the current language setting. In its simplest form, the
keyword refers to "right here": `context` keyword refers to "right here":
```example ```example
#set text(lang: "de") #set text(lang: "de")
// query the language setting "here" // read the language setting "here"
#context text.lang #context text.lang
``` ```
Note that calling `#text.lang` directly would be an error, because the request Note that any attempt to access `#text.lang` directly, i.e. outside of a context,
cannot be answered without knowledge of the context. The field names supported will cause the compiler to issue an error message. The field names supported
by a given element function always correspond to the named parameters documented by a given element function always correspond to the named parameters documented
on each element's page. on each element's page.
Moreover, some functions, such as [`to-absolute`]($length.to-absolute) Moreover, some functions, such as [`to-absolute`]($length.to-absolute)
and [`counter.display`]($counter.display) are only applicable in a context, and [`counter.display`]($counter.display), are only applicable in a context,
because their results depend on the current settings of style properties. because their results depend on the current settings of style properties.
When another function `foo()` calls a context-dependent function, it becomes When another function `foo()` calls a context-dependent function, it becomes
itself context-dependent: itself context-dependent:
@ -54,12 +54,14 @@ itself context-dependent:
```example ```example
#let foo() = 1em.to-absolute() #let foo() = 1em.to-absolute()
#context { #context {
// foo() cannot be called outside of a context // foo() cannot be called
// outside of a context
foo() == text.size foo() == text.size
} }
``` ```
When a property is changed, the response to the query changes accordingly: When a property is changed, the response to the property access
changes accordingly:
```example ```example
#set text(lang: "en") #set text(lang: "en")
@ -69,61 +71,68 @@ When a property is changed, the response to the query changes accordingly:
#context text.lang #context text.lang
``` ```
The output of a `#context ...` call is static in the form of opaque As you see, the result of a `#context ...` expression can
[`content`]. Write access to context output is prohibited, as it would be inserted into the document as `content`. Context blocks can
often result in invalid code: If the context changes between read and contain arbitrary code beyond the field access. However,
write, overwriting a property would cause an inconsistent system state. and this is often surprisingly for newcomers, context-dependent
In fact, context-dependent property fields are immutable constants even property fields remain _constant_ throughout the context's scope.
within the context itself: This has two important consequences: First, direct property
assignments like `text.lang = "de"` are _not_ allowed –
always use `set` or `show` rules. Second, changes to a
property value within a context (e.g. by a `set` rule) are not
observable by field access within that same context:
```example ```example
#set text(lang: "en") #set text(lang: "en")
#context [ #context [
call 1: #text.lang \ Read 1: #text.lang
#set text(lang: "fr") #set text(lang: "fr")
call 2: #text.lang Read 2: #text.lang
] ]
``` ```
Both calls have the same output 'en', because `text.lang` is assigned Both reads have the same output `"en"`, because `text.lang` is assigned
upon entry in the context and remains constant until the end of its scope upon entry in the context and remains constant until the end of its scope
(the closing `]`). It does not "see" the `#set text(lang: "fr")` before (the closing `]`). Thus, the `text.lang` field cannot "see" the effect
call 2. Compare this to the previous example: there we got two different of `#set text(lang: "fr")`, although Read 2 occurs afterwards. Compare
results because we created two different contexts. this to the previous example: There we got two different results because
we created two different contexts.
However, immutability only applies to the property fields themselves. However, immutability only applies to the property fields themselves.
Content creation instructions within a context _do_ see the effect of The appearance of content within a context _can_ be changed in the
the set rule. Consider the same example with font size: usual manner. e.g. by set rules. Consider the same example with font size:
```example ```example
#set text(size: 50pt) #set text(size: 40pt)
#context [ #context [
call 1: #text.size \ Read 1: #text.size \
#set text(size: 25pt) #set text(size: 25pt)
call 2: #text.size Read 2: #text.size
] ]
``` ```
Call 2 still outputs '50pt', because `text.size` is a constant. Read 2 still outputs `40pt`, because `text.size` is a constant.
However, this output is printed in '25pt' font, as specified by the set However, this output is printed in 25pt font, as specified by the set
rule before the call. This illustrates the importance of picking the rule before the read. This illustrates the importance of picking the
right insertion point for a context to get access to precisely the right right insertion point for a context to get access to precisely the right
styles. If you need access to updated property fields after a set rule, styles. If you need access to updated property fields after a set rule,
you can use nested contexts: you can use _nested contexts_:
```example ```example
#set text(lang: "en") #set text(lang: "en")
#context [ #context [
call 1: #text.lang \ Read 1: #text.lang \
#set text(lang: "fr") #set text(lang: "fr")
call 2: #context text.lang Read 2: #context text.lang
] ]
``` ```
All of the above applies to `show` rules analogously, for example: All of the above applies to `show` rules analogously. To demonstrate this,
we define a function `template` which is activated by an "everything" set
rule in a context:
```example ```example
#let template(body) = { #let template(body) = {
@ -131,73 +140,85 @@ All of the above applies to `show` rules analogously, for example:
body body
} }
#set text(size: 50pt) #set text(size: 40pt)
#context [ #context [
call 1: #text.size \ Read 1: #text.size
#show: template #show: template
call 2: #text.size \ Read 2: #text.size \
call 3: #context text.size Read 3: #context text.size
] ]
``` ```
Reads 1 and 2 print the original text size upon entry in the first
context (since `text.size` remains constant there), but Read 3 is
located in a nested context and reflects the new font size set by
the `show` rule via the `template` function.
## Controlling content creation within a context ## Using context-dependent property fields to control content appearance
An important purpose of reading the current value of properties is,
The main purpose of retrieving the current values of properties is, of course, to use this information in the calculation of derived
of course, to use them in the calculation of derived properties, properties, instead of setting those properties manually. For example,
instead of setting those properties manually. For example, you can you can double the font size like this:
double the font size like this:
```example ```example
#context { #context [
// the context allows you to // the context allows you to
// retrieve the current text.size // retrieve the current text.size
set text(size: text.size * 200%) #set text(size: text.size * 200%)
[large text \ ] Large text \
} ]
original size Original size
``` ```
Since set rules are only active until the end of the enclosing scope, Since set rules are only active until the end of the enclosing scope,
'original size' is printed with the original font size. "Original size" is printed with the original font size.
The above example is equivalent to The above example is equivalent to
```example ```example
#{ #[
set text(size: 2em) #set text(size: 2em)
[large text \ ] Large text \
} ]
original size Original size
``` ```
but convenient alternatives like this do not exist for most properties. but convenient alternatives like this are unavailable for most properties.
This makes contexts a powerful and versatile concept. For example, This makes contexts a powerful and versatile concept. For example,
to double the spacing between the lines of an equation block, you can you can use a similar resizing technique to increase the spacing
use the same resizing technique in a show rule. In this case, explicitly between the lines of a specific equation block (or any other content):
adding the `context` keyword is not necessary, because a show rule
establishes a context automatically:
```example ```example
#let spaced-eq(spacing: 100%, body) = { #let spaced(spacing: 100%, body) = context {
show math.equation.where(block: true): it => { // access current par.leading in a context
// access current par.leading in the set par(leading: par.leading * spacing)
// context of the show rule
set par(leading: par.leading * spacing)
it
}
body body
} }
normal spacing: Normal spacing:
$ $ x \ x $
x \ Doubled spacing:
x #spaced(spacing: 200%)[$ z \ z $]
$ ```
doubled spacing:
#spaced-eq(spacing: 200%)[$ The advantage of this technique is that the user does not have to know the
z \ original spacing in order to double it. To double the spacing of all
z equations, you can put the same calculations in a `show` rule. Note that
$] it is not necessary to add the `context` keyword on the right-hand side
of a `show` rule, because show rules establish a context automatically:
```example
Normal spacing:
$ x \ x $
#show math.equation.where(block: true): it => {
// access current par.leading in a context,
// established automatically by the show rule
set par(leading: par.leading * 200%)
it
}
Doubled spacing:
$ z \ z $
``` ```
## Location context ## Location context
@ -262,9 +283,9 @@ demonstrates this:
The rule that context-dependent variables and functions remain constant The rule that context-dependent variables and functions remain constant
within a given `context` also applies to location context. The function within a given `context` also applies to location context. The function
`counter.display()` is an example for this behavior. Below, call A will `counter.display()` is an example for this behavior. Below, read A will
access the counter's value upon _entry_ into the context, i.e. '1' - it access the counter's value upon _entry_ into the context, i.e. `1` - it
cannot see the effect of `{c.update(2)}`. In contrast, call B accesses cannot see the effect of `{c.update(2)}`. In contrast, read B accesses
the counter in a nested context and will thus see the updated value. the counter in a nested context and will thus see the updated value.
```example ```example
@ -272,8 +293,8 @@ the counter in a nested context and will thus see the updated value.
#c.update(1) #c.update(1)
#context [ #context [
#c.update(2) #c.update(2)
call A: #c.display() \ Read A: #c.display() \
call B: #context c.display() Read B: #context c.display()
] ]
``` ```