From 3dea9b39146d915b7c61e3a03562228cda28a067 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 15:12:41 +0100 Subject: [PATCH 01/19] First attempt at documentation rewording --- docs/reference/language/context.md | 48 +++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index bdd520f5d..38e024f6b 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -26,37 +26,51 @@ in some places that are also aware of their location in the document: outline, for instance, also provide the proper context to resolve counters. ## Style context -With set rules, we can adjust style properties for parts or the whole of our -document. We cannot access these without a known context, as they may change -throughout the course of the document. When context is available, we can -retrieve them simply by accessing them as fields on the respective element -function. +Style properties often change within a document, for example by applying set +rules. Consequently, to retrieve settings one must first specify the context +where the query is to be executed. Within a given context, the property +information is provided by a simple field access syntax. For example, +`text.lang` asks for the current language setting. In its simplest form, the +`context` keyword refers to "right here": ```example #set text(lang: "de") #context text.lang ``` -As explained above, a context expression is reactive to the different -environments it is placed into. In the example below, we create a single context -expression, store it in the `value` variable and use it multiple times. Each use -properly reacts to the current surroundings. +Note that calling `#text.lang` directly would be an error, because the request +cannot be answered without knowledge of the context. The fields supported by +a given element function are documented **where??** and can be retrieved in a +document by calling **what?? (something like `fields()`)**. + +When the language setting changes, the responses to the query change accordingly: +_Remark: The old example with `#let value = context text.lang` is very confusing at +this early stage of the explanation and does more harm than good._ ```example -#let value = context text.lang -#value +#context text.lang #set text(lang: "de") -#value +#context text.lang #set text(lang: "fr") -#value +#context text.lang ``` -Crucially, upon creation, `value` becomes opaque [content] that we cannot peek -into. It can only be resolved when placed somewhere because only then the -context is known. The body of a context expression may be evaluated zero, one, -or multiple times, depending on how many different places it is put into. +The output of a `#context ...` call is _read-only_ (in the form of `[content]`). +Allowing write access would likely result in invalid code, because the context +might have already changed in the meantime. Therefore, temporary changes of +settings must be done within the context, and they are only active until the +end of the context's scope: + +```example +#context { + // the context allows you to retrieve the current text.size + set text(size: text.size * 200%) + [large text] +} +original size +``` ## Location context We've already seen that context gives us access to set rule values. But it can From e1244479ace7cf3144acf236cee39763deac59c7 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 20:39:08 +0100 Subject: [PATCH 02/19] Revised the context documentation ... with some open questions to be discussed. --- docs/reference/language/context.md | 259 +++++++++++++++++++---------- 1 file changed, 169 insertions(+), 90 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 38e024f6b..2ce06dd3b 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -25,53 +25,182 @@ in some places that are also aware of their location in the document: [Show rules]($styling/#show-rules) provide context[^1] and numberings in the outline, for instance, also provide the proper context to resolve counters. -## Style context -Style properties often change within a document, for example by applying set -rules. Consequently, to retrieve settings one must first specify the context -where the query is to be executed. Within a given context, the property -information is provided by a simple field access syntax. For example, -`text.lang` asks for the current language setting. In its simplest form, the -`context` keyword refers to "right here": +## Behavior of the context keyword +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 +the context where the query is to be executed. This is the purpose of the +`context` keyword. Once the context has been fixed, the property information +is available through a simple field access syntax. For example, `text.lang` +asks for the current language setting. In its simplest form, the `context` +keyword refers to "right here": ```example #set text(lang: "de") +// query the language setting "here" #context text.lang ``` Note that calling `#text.lang` directly would be an error, because the request -cannot be answered without knowledge of the context. The fields supported by -a given element function are documented **where??** and can be retrieved in a -document by calling **what?? (something like `fields()`)**. +cannot be answered without knowledge of the context. The field names supported +by a given element function **always??|usually??** correspond to the names of +the optional arguments in the element's constructor. +_Remark: it would be nice to have a `.fields()` function for all types, +not just content._ -When the language setting changes, the responses to the query change accordingly: -_Remark: The old example with `#let value = context text.lang` is very confusing at -this early stage of the explanation and does more harm than good._ +Moreover, some functions, such as [`to-absolute`]($length.to-absolute) +**(more examples)** are only applicable in a context, because their +results depend on the current settings of style properties. When another +function `foo()` calls a context-dependent function, it becomes itself +context-dependent: ```example +#let foo() = 1em.to-absolute() +#context { + // foo() cannot be called outside of a context + foo() == text.size +} +``` + +When a property is changed, the response to the query changes accordingly: + +```example +#set text(lang: "en") #context text.lang #set text(lang: "de") #context text.lang - -#set text(lang: "fr") -#context text.lang ``` -The output of a `#context ...` call is _read-only_ (in the form of `[content]`). -Allowing write access would likely result in invalid code, because the context -might have already changed in the meantime. Therefore, temporary changes of -settings must be done within the context, and they are only active until the -end of the context's scope: +The output of a `#context ...` call is _read-only_ in the form of opaque +`[content]`. Write access is prohibited, as it would often result in +invalid code: If the context changes between read and write, overwriting +a property would cause an inconsistent system state. In fact, +context-dependent property fields are read-only even within the context +itself: + +```example +#set text(lang: "en") +#context [ + call 1: #text.lang \ + + #set text(lang: "fr") + call 2: #text.lang +] +``` + +Both calls have the same output 'en', because `text.lang` is assigned +upon entry in the context and remains constant until the end of its scope +(the closing `]`). Compare this to the previous example: there we +got two different results because we created two different contexts. + +However, the read-only restriction only applies to the property fields +themselves. Content creation instructions _do_ see the effect of the +set rule. Consider the same example with font size: + +```example +#set text(size: 50pt) +#context [ + call 1: #text.size \ + + #set text(size: 25pt) + call 2: #text.size +] +``` + +The second call still outputs '50pt', because `text.size` is a constant. +However, this output is printed in '25pt' font, as specified by the set +rule before the call. This illustrates the importance of picking the +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, you can +use nested contexts: + +```example +#set text(lang: "en") +#context [ + call 1: #text.lang \ + + #set text(lang: "fr") + call 2: #context text.lang +] +``` + +All of the above applies to `show` rules analogously, for example: + +```example +#let template(body) = { + set text(size: 25pt) + body +} + +#set text(size: 50pt) +#context [ + call 1: #text.size \ + + #show: template + call 2: #text.size \ + call 3: #context text.size +] +``` + +## Controlling content creation within a context + +The main purpose of retrieving the current values of properties is, +of course, to use them in the calculation of derived properties, +instead of setting those properties manually. For example, you can +double the font size like this: ```example #context { - // the context allows you to retrieve the current text.size + // the context allows you to + // retrieve the current text.size set text(size: text.size * 200%) - [large text] + [large text \ ] } original size ``` +Since set rules are only active until the end of the enclosing scope, +'original size' is printed with the original font size. +The above example is equivalent to + +```example +#{ + set text(size: 2em) + [large text \ ] +} +original size +``` + +but convenient alternatives like this do not exist for most properties. +For example, to double the spacing between the lines of an equation block, +you can use the same technique in a show rule. In this case, explicitly +adding the `context` keyword is not necessary, because a show rule +establishes a context automatically: + +```example +#let spaced-eq(spacing: 100%, body) = { + show math.equation.where(block: true): it => { + // access current par.leading in the + // context of the show rule + set par(leading: par.leading * spacing) + it + } + body +} + +normal spacing: +$ +x \ +x +$ +doubled spacing: +#spaced-eq(spacing: 200%)[$ +z \ +z +$] +``` + ## Location context We've already seen that context gives us access to set rule values. But it can do more: It also lets us know _where_ in the document we currently are, relative @@ -132,6 +261,23 @@ demonstrates this: ] ``` +The rule that context-dependent variables and functions remain constant +within a given `context` also applies to location context. The function +`counter.display()` is an example for this behavior. Below, call A will +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 +the counter in a nested context and will thus see the updated value. + +```example +#let c = counter("mycounter") +#c.update(1) +#context [ + #c.update(2) + call A: #c.display() \ + call B: #context c.display() +] +``` + As mentioned before, we can also use context to get the physical position of elements on the pages. We do this with the [`locate`] function, which works similarly to `counter.at`: It takes a location or other [selector] that resolves @@ -154,73 +300,6 @@ There are other functions that make use of the location context, most prominently [`query`]. Take a look at the [introspection]($category/introspection) category for more details on those. -## Nested contexts -Context is also accessible from within function calls nested in context blocks. -In the example below, `foo` itself becomes a contextual function, just like -[`to-absolute`]($length.to-absolute) is. - -```example -#let foo() = 1em.to-absolute() -#context { - foo() == text.size -} -``` - -Context blocks can be nested. Contextual code will then always access the -innermost context. The example below demonstrates this: The first `text.lang` -will access the outer context block's styles and as such, it will **not** -see the effect of `{set text(lang: "fr")}`. The nested context block around the -second `text.lang`, however, starts after the set rule and will thus show -its effect. - -```example -#set text(lang: "de") -#context [ - #set text(lang: "fr") - #text.lang \ - #context text.lang -] -``` - -You might wonder why Typst ignores the French set rule when computing the first -`text.lang` in the example above. The reason is that, in the general case, Typst -cannot know all the styles that will apply as set rules can be applied to -content after it has been constructed. Below, `text.lang` is already computed -when the template function is applied. As such, it cannot possibly be aware of -the language change to French in the template. - -```example -#let template(body) = { - set text(lang: "fr") - upper(body) -} - -#set text(lang: "de") -#context [ - #show: template - #text.lang \ - #context text.lang -] -``` - -The second `text.lang`, however, _does_ react to the language change because -evaluation of its surrounding context block is deferred until the styles for it -are known. This illustrates the importance of picking the right insertion point for a context to get access to precisely the right styles. - -The same also holds true for the location context. Below, the first -`{c.display()}` call will access the outer context block and will thus not see -the effect of `{c.update(2)}` while the second `{c.display()}` accesses the inner context and will thus see it. - -```example -#let c = counter("mycounter") -#c.update(1) -#context [ - #c.update(2) - #c.display() \ - #context c.display() -] -``` - ## Compiler iterations To resolve contextual interactions, the Typst compiler processes your document multiple times. For instance, to resolve a `locate` call, Typst first provides a From 262ec96a905dc99506cc75eee2b9ec1aa2514451 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 20:47:56 +0100 Subject: [PATCH 03/19] Incorporated suggestions by @MDLC01 --- docs/reference/language/context.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 2ce06dd3b..1967b5cff 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -42,16 +42,14 @@ keyword refers to "right here": Note that calling `#text.lang` directly would be an error, because the request cannot be answered without knowledge of the context. The field names supported -by a given element function **always??|usually??** correspond to the names of -the optional arguments in the element's constructor. -_Remark: it would be nice to have a `.fields()` function for all types, -not just content._ +by a given element function always correspond to the named parameters documented +on each element's page. Moreover, some functions, such as [`to-absolute`]($length.to-absolute) -**(more examples)** are only applicable in a context, because their -results depend on the current settings of style properties. When another -function `foo()` calls a context-dependent function, it becomes itself -context-dependent: +and [`counter.display`]($counter.display) are only applicable in a context, +because their results depend on the current settings of style properties. +When another function `foo()` calls a context-dependent function, it becomes +itself context-dependent: ```example #let foo() = 1em.to-absolute() @@ -72,7 +70,7 @@ When a property is changed, the response to the query changes accordingly: ``` The output of a `#context ...` call is _read-only_ in the form of opaque -`[content]`. Write access is prohibited, as it would often result in +`content`. Write access is prohibited, as it would often result in invalid code: If the context changes between read and write, overwriting a property would cause an inconsistent system state. In fact, context-dependent property fields are read-only even within the context From 7f9ddfbd80b95363e02d0b7872297cd064ee2377 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 21:04:44 +0100 Subject: [PATCH 04/19] Rephrased 'read-only' as recommended by @MDLC01 --- docs/reference/language/context.md | 37 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 1967b5cff..b0fab89ac 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -69,12 +69,12 @@ When a property is changed, the response to the query changes accordingly: #context text.lang ``` -The output of a `#context ...` call is _read-only_ in the form of opaque -`content`. Write access is prohibited, as it would often result in -invalid code: If the context changes between read and write, overwriting -a property would cause an inconsistent system state. In fact, -context-dependent property fields are read-only even within the context -itself: +The output of a `#context ...` call is static in the form of opaque +`content`. Write access to context output is prohibited, as it would +often result in invalid code: If the context changes between read and +write, overwriting a property would cause an inconsistent system state. +In fact, context-dependent property fields are immutable constants even +within the context itself: ```example #set text(lang: "en") @@ -88,12 +88,13 @@ itself: Both calls have the same output 'en', because `text.lang` is assigned upon entry in the context and remains constant until the end of its scope -(the closing `]`). Compare this to the previous example: there we -got two different results because we created two different contexts. +(the closing `]`). It does not "see" the `#set text(lang: "fr")` before +call 2. Compare this to the previous example: there we got two different +results because we created two different contexts. -However, the read-only restriction only applies to the property fields -themselves. Content creation instructions _do_ see the effect of the -set rule. Consider the same example with font size: +However, immutability only applies to the property fields themselves. +Content creation instructions within a context _do_ see the effect of +the set rule. Consider the same example with font size: ```example #set text(size: 50pt) @@ -105,13 +106,12 @@ set rule. Consider the same example with font size: ] ``` -The second call still outputs '50pt', because `text.size` is a constant. +Call 2 still outputs '50pt', because `text.size` is a constant. 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 call. This illustrates the importance of picking the 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, you can -use nested contexts: +styles. If you need access to updated property fields after a set rule, +you can use nested contexts: ```example #set text(lang: "en") @@ -171,8 +171,9 @@ original size ``` but convenient alternatives like this do not exist for most properties. -For example, to double the spacing between the lines of an equation block, -you can use the same technique in a show rule. In this case, explicitly +This makes contexts a powerful and versatile concept. For example, +to double the spacing between the lines of an equation block, you can +use the same resizing technique in a show rule. In this case, explicitly adding the `context` keyword is not necessary, because a show rule establishes a context automatically: From db7ba6fe69b0d1fc97e7ce2bdb1083b3056a9a67 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 21:20:27 +0100 Subject: [PATCH 05/19] changed [`content`] --- docs/reference/language/context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index b0fab89ac..da80ed5af 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -70,7 +70,7 @@ When a property is changed, the response to the query changes accordingly: ``` The output of a `#context ...` call is static in the form of opaque -`content`. Write access to context output is prohibited, as it would +[`content`]. Write access to context output is prohibited, as it would often result in invalid code: If the context changes between read and write, overwriting a property would cause an inconsistent system state. In fact, context-dependent property fields are immutable constants even From e4286fbe9b5fefb06899d153b7d8cecc92fadd49 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 21:51:54 +0100 Subject: [PATCH 06/19] Minor edits in the scripting documentation --- docs/reference/language/scripting.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index 5e0f1555e..7c66728ac 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -157,6 +157,8 @@ resulting from the else's body. ] ``` +Note that the opening brace or bracket of the body must be on the same +line as the `#if` (unlike in many other languages). Each branch can have a code or content block as its body. - `{if condition {..}}` @@ -187,6 +189,8 @@ together into one larger array. } ``` +Note that the opening brace or bracket of the body must be on the same +line as the `#for` or `#whilde` (unlike in many other languages). For loops can iterate over a variety of collections: - `{for value in array {..}}` \ @@ -245,6 +249,10 @@ The value in question can be either: [element function]($function/#element-functions) that were given when the element was constructed. +**Important:** Many fields of element functions are only visible and accessible +in a concrete context that disambiguates their meaning. Consult the chapter +on [Context] for more information. + ```example #let it = [= Heading] #it.body \ From 5299fb29171181ff37833f18ddc3e5d9441a17a0 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 22:04:32 +0100 Subject: [PATCH 07/19] Fixed link --- docs/reference/language/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index 7c66728ac..8195f8594 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -251,7 +251,7 @@ The value in question can be either: **Important:** Many fields of element functions are only visible and accessible in a concrete context that disambiguates their meaning. Consult the chapter -on [Context] for more information. +on [Context]($context) for more information. ```example #let it = [= Heading] From 574fffe9ae223b42a9885836794f8777d48266f3 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 22:26:03 +0100 Subject: [PATCH 08/19] Fixed typo --- docs/reference/language/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index 8195f8594..c40fe40ec 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -190,7 +190,7 @@ together into one larger array. ``` Note that the opening brace or bracket of the body must be on the same -line as the `#for` or `#whilde` (unlike in many other languages). +line as the `#for` or `#while` (unlike in many other languages). For loops can iterate over a variety of collections: - `{for value in array {..}}` \ From 52cccce5bb7ab50c9a42db952c435f117a4df584 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 4 Mar 2025 23:49:59 +0100 Subject: [PATCH 09/19] Minor clarification --- docs/reference/language/scripting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index c40fe40ec..c9ee6350c 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -249,8 +249,8 @@ The value in question can be either: [element function]($function/#element-functions) that were given when the element was constructed. -**Important:** Many fields of element functions are only visible and accessible -in a concrete context that disambiguates their meaning. Consult the chapter +**Important:** Many fields of element functions are only accessible in a +concrete context that disambiguates their meaning. Consult the chapter on [Context]($context) for more information. ```example From c472ee382e159ae2f9823c29f457a292ded8ab9e Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Wed, 5 Mar 2025 00:20:09 +0100 Subject: [PATCH 10/19] Added explanation of identifiers --- docs/reference/language/scripting.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index c9ee6350c..d5b63142f 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -7,6 +7,16 @@ Typst embeds a powerful scripting language. You can automate your documents and create more sophisticated styles with code. Below is an overview over the scripting concepts. +## Identifiers + +Typst uses largely the same rules to define variables, function names etc. +as most other languages, with one important exception: dashes are permitted +as part of an identifier, e.g. the [`to-absolute`]($length.to-absolute) +function for a length or the [`first-line-indent`]($par.first-line-indent) +parameter of a paragraph. In fact, this convention is more common than +underscores or camel case. Since the dash is also the symbol for subtraction, +a minus must be disambiguated by surrounding white space in script mode. + ## Expressions In Typst, markup and code are fused into one. All but the most common elements are created with _functions._ To make this as convenient as possible, Typst From a9a8426d2d9335047ab3920b036306d30553ac96 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 11 Mar 2025 19:05:15 +0100 Subject: [PATCH 11/19] Revised context.md according to github discussion --- docs/reference/language/context.md | 187 ++++++++++++++++------------- 1 file changed, 104 insertions(+), 83 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index da80ed5af..3040edfda 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -28,25 +28,25 @@ outline, for instance, also provide the proper context to resolve counters. ## Behavior of the context keyword 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 -the context where the query is to be executed. This is the purpose of the -`context` keyword. Once the context has been fixed, the property information -is available through a simple field access syntax. For example, `text.lang` -asks for the current language setting. In its simplest form, the `context` -keyword refers to "right here": +the precise context where the property should be retrieved. This can be achieved +with the `context` keyword. Once the context has been fixed, the property +information is available through standard field access syntax. For example, +`text.lang` asks for the current language setting. In its simplest form, the +`context` keyword refers to "right here": ```example #set text(lang: "de") -// query the language setting "here" +// read the language setting "here" #context text.lang ``` -Note that calling `#text.lang` directly would be an error, because the request -cannot be answered without knowledge of the context. The field names supported +Note that any attempt to access `#text.lang` directly, i.e. outside of a context, +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 on each element's page. 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. When another function `foo()` calls a context-dependent function, it becomes itself context-dependent: @@ -54,12 +54,14 @@ itself context-dependent: ```example #let foo() = 1em.to-absolute() #context { - // foo() cannot be called outside of a context + // foo() cannot be called + // outside of a context 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 #set text(lang: "en") @@ -69,61 +71,68 @@ When a property is changed, the response to the query changes accordingly: #context text.lang ``` -The output of a `#context ...` call is static in the form of opaque -[`content`]. Write access to context output is prohibited, as it would -often result in invalid code: If the context changes between read and -write, overwriting a property would cause an inconsistent system state. -In fact, context-dependent property fields are immutable constants even -within the context itself: +As you see, the result of a `#context ...` expression can +be inserted into the document as `content`. Context blocks can +contain arbitrary code beyond the field access. However, +and this is often surprisingly for newcomers, context-dependent +property fields remain _constant_ throughout the context's scope. +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 #set text(lang: "en") #context [ - call 1: #text.lang \ + Read 1: #text.lang #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 -(the closing `]`). It does not "see" the `#set text(lang: "fr")` before -call 2. Compare this to the previous example: there we got two different -results because we created two different contexts. +(the closing `]`). Thus, the `text.lang` field cannot "see" the effect +of `#set text(lang: "fr")`, although Read 2 occurs afterwards. Compare +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. -Content creation instructions within a context _do_ see the effect of -the set rule. Consider the same example with font size: +The appearance of content within a context _can_ be changed in the +usual manner. e.g. by set rules. Consider the same example with font size: ```example -#set text(size: 50pt) +#set text(size: 40pt) #context [ - call 1: #text.size \ + Read 1: #text.size \ #set text(size: 25pt) - call 2: #text.size + Read 2: #text.size ] ``` -Call 2 still outputs '50pt', because `text.size` is a constant. -However, this output is printed in '25pt' font, as specified by the set -rule before the call. This illustrates the importance of picking the +Read 2 still outputs `40pt`, because `text.size` is a constant. +However, this output is printed in 25pt font, as specified by the set +rule before the read. This illustrates the importance of picking the 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, -you can use nested contexts: +you can use _nested contexts_: ```example #set text(lang: "en") #context [ - call 1: #text.lang \ + Read 1: #text.lang \ #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 #let template(body) = { @@ -131,73 +140,85 @@ All of the above applies to `show` rules analogously, for example: body } -#set text(size: 50pt) +#set text(size: 40pt) #context [ - call 1: #text.size \ + Read 1: #text.size #show: template - call 2: #text.size \ - call 3: #context text.size + Read 2: #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 - -The main purpose of retrieving the current values of properties is, -of course, to use them in the calculation of derived properties, -instead of setting those properties manually. For example, you can -double the font size like this: +## Using context-dependent property fields to control content appearance +An important purpose of reading the current value of properties is, +of course, to use this information in the calculation of derived +properties, instead of setting those properties manually. For example, +you can double the font size like this: ```example -#context { +#context [ // the context allows you to // retrieve the current text.size - set text(size: text.size * 200%) - [large text \ ] -} -original size + #set text(size: text.size * 200%) + Large text \ +] +Original size ``` 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 ```example -#{ - set text(size: 2em) - [large text \ ] -} -original size +#[ + #set text(size: 2em) + Large text \ +] +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, -to double the spacing between the lines of an equation block, you can -use the same resizing technique in a show rule. In this case, explicitly -adding the `context` keyword is not necessary, because a show rule -establishes a context automatically: +you can use a similar resizing technique to increase the spacing +between the lines of a specific equation block (or any other content): ```example -#let spaced-eq(spacing: 100%, body) = { - show math.equation.where(block: true): it => { - // access current par.leading in the - // context of the show rule - set par(leading: par.leading * spacing) - it - } +#let spaced(spacing: 100%, body) = context { + // access current par.leading in a context + set par(leading: par.leading * spacing) body } -normal spacing: -$ -x \ -x -$ -doubled spacing: -#spaced-eq(spacing: 200%)[$ -z \ -z -$] +Normal spacing: +$ x \ x $ +Doubled spacing: +#spaced(spacing: 200%)[$ z \ z $] +``` + +The advantage of this technique is that the user does not have to know the +original spacing in order to double it. To double the spacing of all +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 @@ -262,9 +283,9 @@ demonstrates this: The rule that context-dependent variables and functions remain constant within a given `context` also applies to location context. The function -`counter.display()` is an example for this behavior. Below, call A will -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 +`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 +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. ```example @@ -272,8 +293,8 @@ the counter in a nested context and will thus see the updated value. #c.update(1) #context [ #c.update(2) - call A: #c.display() \ - call B: #context c.display() + Read A: #c.display() \ + Read B: #context c.display() ] ``` From 293ba5631f83d836a2ed7ffe069cacba6e0d33df Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 11 Mar 2025 19:11:48 +0100 Subject: [PATCH 12/19] Revised scripting.md according to github discussion --- docs/reference/language/scripting.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index d5b63142f..3f0f6e8ac 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -8,14 +8,14 @@ create more sophisticated styles with code. Below is an overview over the scripting concepts. ## Identifiers - Typst uses largely the same rules to define variables, function names etc. as most other languages, with one important exception: dashes are permitted as part of an identifier, e.g. the [`to-absolute`]($length.to-absolute) function for a length or the [`first-line-indent`]($par.first-line-indent) -parameter of a paragraph. In fact, this convention is more common than -underscores or camel case. Since the dash is also the symbol for subtraction, -a minus must be disambiguated by surrounding white space in script mode. +parameter of a paragraph. In fact, this is the canonical convention – +underscores or camel case are not used in "official" typst. Since the dash +is also the symbol for subtraction, a minus must be disambiguated by +surrounding white space in script mode. ## Expressions In Typst, markup and code are fused into one. All but the most common elements @@ -168,7 +168,7 @@ resulting from the else's body. ``` Note that the opening brace or bracket of the body must be on the same -line as the `#if` (unlike in many other languages). +line as the `#if` and `else` (unlike in many other languages). Each branch can have a code or content block as its body. - `{if condition {..}}` @@ -259,9 +259,9 @@ The value in question can be either: [element function]($function/#element-functions) that were given when the element was constructed. -**Important:** Many fields of element functions are only accessible in a -concrete context that disambiguates their meaning. Consult the chapter -on [Context]($context) for more information. +**Important:** Many fields of element functions are only accessible in a +`context` where their concrete values are known unambiguously. Consult the +chapter on [Context]($context) for more information. ```example #let it = [= Heading] From 4ab102e7baa89629032ecb4d42a6c7a09bf84ee6 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 11 Mar 2025 23:30:34 +0100 Subject: [PATCH 13/19] Incorporated more suggestions by @MDLC01 --- docs/reference/language/context.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 3040edfda..1ec22f517 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -78,7 +78,7 @@ and this is often surprisingly for newcomers, context-dependent property fields remain _constant_ throughout the context's scope. 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 +use `set` or `show` rules for this purpose. 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: @@ -94,19 +94,19 @@ observable by field access within that same context: 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 -(the closing `]`). Thus, the `text.lang` field cannot "see" the effect -of `#set text(lang: "fr")`, although Read 2 occurs afterwards. Compare +(the closing `]`). Thus, the `text.lang` field is not affected by +#set text(lang: "fr")`, although Read 2 occurs afterwards. Compare 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. The appearance of content within a context _can_ be changed in the -usual manner. e.g. by set rules. Consider the same example with font size: +usual manner, e.g. by set rules. Consider the same example with font size: ```example #set text(size: 40pt) #context [ - Read 1: #text.size \ + Read 1: #text.size #set text(size: 25pt) Read 2: #text.size @@ -123,15 +123,15 @@ you can use _nested contexts_: ```example #set text(lang: "en") #context [ - Read 1: #text.lang \ + Read 1: #text.lang #set text(lang: "fr") Read 2: #context text.lang ] ``` -All of the above applies to `show` rules analogously. To demonstrate this, -we define a function `template` which is activated by an "everything" set +All of the above apply to `show` rules analogously. To demonstrate this, +we define a function `template` which is activated by an "everything" show rule in a context: ```example @@ -202,7 +202,7 @@ Doubled spacing: The advantage of this technique is that the user does not have to know the original spacing in order to double it. To double the spacing of all -equations, you can put the same calculations in a `show` rule. Note that +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: @@ -283,9 +283,9 @@ demonstrates this: The rule that context-dependent variables and functions remain constant within a given `context` also applies to location context. The function -`counter.display()` is an example for this behavior. Below, read 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 -cannot see the effect of `{c.update(2)}`. In contrast, read 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. ```example From f6ce8627c2abb5a643f6443017d4b125aebfa585 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 11 Mar 2025 23:31:22 +0100 Subject: [PATCH 14/19] Fixed spelling --- docs/reference/language/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index 3f0f6e8ac..829d368b3 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -9,7 +9,7 @@ scripting concepts. ## Identifiers Typst uses largely the same rules to define variables, function names etc. -as most other languages, with one important exception: dashes are permitted +as most other languages, with one important exception: Dashes are permitted as part of an identifier, e.g. the [`to-absolute`]($length.to-absolute) function for a length or the [`first-line-indent`]($par.first-line-indent) parameter of a paragraph. In fact, this is the canonical convention – From 0644ec0bb9441c89afa72010b0c4a5a404136fd2 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Wed, 12 Mar 2025 00:38:24 +0100 Subject: [PATCH 15/19] Changed apply => applies --- docs/reference/language/context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 1ec22f517..b3ae143ab 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -130,7 +130,7 @@ you can use _nested contexts_: ] ``` -All of the above apply to `show` rules analogously. To demonstrate this, +All of the above applies to `show` rules analogously. To demonstrate this, we define a function `template` which is activated by an "everything" show rule in a context: From 397b663907eb3644cc30f05f3a324a9f07106e54 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Wed, 19 Mar 2025 16:50:08 +0100 Subject: [PATCH 16/19] Rewording (suggested by @PgBiel) Co-authored-by: PgBiel <9021226+PgBiel@users.noreply.github.com> --- docs/reference/language/context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index b3ae143ab..7f0084edb 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -172,7 +172,7 @@ Original size Since set rules are only active until the end of the enclosing scope, "Original size" is printed with the original font size. -The above example is equivalent to +For the specific case of accessing `text.size`, context is usually not necessary as the `1em` unit is always equal to the current font size, so the above example is equivalent to ```example #[ From dace04aa0994aaf11372322051c1d5cfbb6597d7 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Wed, 19 Mar 2025 16:51:25 +0100 Subject: [PATCH 17/19] Fixed typo Co-authored-by: PgBiel <9021226+PgBiel@users.noreply.github.com> --- docs/reference/language/context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index 7f0084edb..a2b532065 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -27,7 +27,7 @@ outline, for instance, also provide the proper context to resolve counters. ## Behavior of the context keyword 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 properties in a consistent way, one must first specify the precise context where the property should be retrieved. This can be achieved with the `context` keyword. Once the context has been fixed, the property information is available through standard field access syntax. For example, From 26cb2446ff71cdb6a4498426b076ebf14ae4a8c9 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 20 Mar 2025 14:24:23 +0100 Subject: [PATCH 18/19] typst -> Typst Co-authored-by: Bastien Voirin --- docs/reference/language/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md index 829d368b3..ea3a62746 100644 --- a/docs/reference/language/scripting.md +++ b/docs/reference/language/scripting.md @@ -13,7 +13,7 @@ as most other languages, with one important exception: Dashes are permitted as part of an identifier, e.g. the [`to-absolute`]($length.to-absolute) function for a length or the [`first-line-indent`]($par.first-line-indent) parameter of a paragraph. In fact, this is the canonical convention – -underscores or camel case are not used in "official" typst. Since the dash +underscores or camel case are not used in "official" Typst. Since the dash is also the symbol for subtraction, a minus must be disambiguated by surrounding white space in script mode. From 2dcd9884514c93646202cdfb5d00ea7f68b578db Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 25 Mar 2025 00:05:09 +0100 Subject: [PATCH 19/19] incorporazed suggestiond by @laurmaedje --- docs/reference/language/context.md | 55 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md index a2b532065..32efd614e 100644 --- a/docs/reference/language/context.md +++ b/docs/reference/language/context.md @@ -25,10 +25,10 @@ in some places that are also aware of their location in the document: [Show rules]($styling/#show-rules) provide context[^1] and numberings in the outline, for instance, also provide the proper context to resolve counters. -## Behavior of the context keyword -Style properties frequently change within a document, for example by applying set +## The context keyword +Style properties frequently change within a document, for example through set rules. To retrieve such properties in a consistent way, one must first specify -the precise context where the property should be retrieved. This can be achieved +the precise context where the property should be retrieved. This is achieved with the `context` keyword. Once the context has been fixed, the property information is available through standard field access syntax. For example, `text.lang` asks for the current language setting. In its simplest form, the @@ -36,26 +36,27 @@ information is available through standard field access syntax. For example, ```example #set text(lang: "de") -// read the language setting "here" +// Read the language setting "here". #context text.lang ``` Note that any attempt to access `#text.lang` directly, i.e. outside of a context, -will cause the compiler to issue an error message. The field names supported +will cause the compiler to issue an error message, since it cannot determine the +precise location the query refers to. The field names supported by a given element function always correspond to the named parameters documented on each element's page. Moreover, some functions, such as [`to-absolute`]($length.to-absolute) and [`counter.display`]($counter.display), are only applicable in a context, 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: ```example #let foo() = 1em.to-absolute() #context { // foo() cannot be called - // outside of a context + // outside of a context. foo() == text.size } ``` @@ -71,13 +72,13 @@ changes accordingly: #context text.lang ``` -As you see, the result of a `#context ...` expression can -be inserted into the document as `content`. Context blocks can +As you see, the result of a `[#context ..]` expression can +be inserted into the document as [content]. Context blocks can contain arbitrary code beyond the field access. However, -and this is often surprisingly for newcomers, context-dependent +and this is often surprising for newcomers, context-dependent property fields remain _constant_ throughout the context's scope. This has two important consequences: First, direct property -assignments like `text.lang = "de"` are _not_ allowed – +assignments like `{text.lang = "de"}` are _not_ allowed – use `set` or `show` rules for this purpose. 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: @@ -92,10 +93,10 @@ observable by field access within that same context: ] ``` -Both reads have the same output `"en"`, because `text.lang` is assigned +Both reads have the same output `{"en"}`, because `text.lang` is fixed upon entry in the context and remains constant until the end of its scope (the closing `]`). Thus, the `text.lang` field is not affected by -#set text(lang: "fr")`, although Read 2 occurs afterwards. Compare +`[#set text(lang: "fr")]`, although Read 2 occurs after it. Compare this to the previous example: There we got two different results because we created two different contexts. @@ -113,7 +114,7 @@ usual manner, e.g. by set rules. Consider the same example with font size: ] ``` -Read 2 still outputs `40pt`, 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 rule before the read. This illustrates the importance of picking the right insertion point for a context to get access to precisely the right @@ -131,8 +132,8 @@ you can use _nested contexts_: ``` All of the above applies to `show` rules analogously. To demonstrate this, -we define a function `template` which is activated by an "everything" show -rule in a context: +we define a function `{template}` (emulating what a document template +might do) which is activated by an "everything" show rule in a context: ```example #let template(body) = { @@ -154,16 +155,16 @@ 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. -## Using context-dependent property fields to control content appearance -An important purpose of reading the current value of properties is, -of course, to use this information in the calculation of derived -properties, instead of setting those properties manually. For example, +## Setting derived properties in a context +An important purpose of reading the current value of properties is +to use this information in the calculation of derived properties, +instead of setting those properties manually. For example, you can double the font size like this: ```example #context [ - // the context allows you to - // retrieve the current text.size + // The context allows you to + // retrieve the current `text.size`. #set text(size: text.size * 200%) Large text \ ] @@ -172,7 +173,9 @@ Original size Since set rules are only active until the end of the enclosing scope, "Original size" is printed with the original font size. -For the specific case of accessing `text.size`, context is usually not necessary as the `1em` unit is always equal to the current font size, so the above example is equivalent to +For the specific case of accessing `text.size`, context is usually +not necessary as the `{1em}` unit is always equal to the current font +size, so the above example is equivalent to ```example #[ @@ -189,7 +192,7 @@ between the lines of a specific equation block (or any other content): ```example #let spaced(spacing: 100%, body) = context { - // access current par.leading in a context + // Access current par.leading in a context. set par(leading: par.leading * spacing) body } @@ -211,8 +214,8 @@ Normal spacing: $ x \ x $ #show math.equation.where(block: true): it => { - // access current par.leading in a context, - // established automatically by the show rule + // Access current par.leading in a context, + // established automatically by the show rule. set par(leading: par.leading * 200%) it }