From ae89b51389ec1e16382f1bf0003be104b1394b74 Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 6 Mar 2025 10:23:43 +0100 Subject: [PATCH 1/5] Added section 'The computational model behind typst' --- docs/reference/language/styling.md | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md index b0b7ab711..cbfff279e 100644 --- a/docs/reference/language/styling.md +++ b/docs/reference/language/styling.md @@ -10,6 +10,56 @@ might not be a built-in property for everything you wish to do. For this reason, Typst further supports _show rules_ that can completely redefine the appearance of elements. +## The computational model behind typst + +Typesetting is controlled by hundreds of parameters, from page margins to font sizes to numbering conventions. Managing this mess is a major focus of every typesetting system. The first step is to arrange these properties into related groups. In typst, these groups are called _element functions_ (EFs), for example `text` for font properties (typeface, size, color etc.), `par` for paragraphs (line spacing, alignment, indentation etc.), and `figure` (placement rules, captioning, and numbering of tables and figures). A complete list of the available element functions is **where??**. The term _functions_ indicates that these entities do not merely act as passive property containers, but can actively process content (usually provided in the function's `body` parameter) according to the present parameter settings. + +Since different parts of the document need different settings for the same parameters (e.g. the font size in headings, plain text, and footnotes), each element function type exists in multiple instances. On the other hand, different EF types must interact to typeset a particular piece of content. You can imagine this as an _ensemble_ of EMs playing together to create the desired output. For example, math rendering requires the `math.equation` EF for equation-specific information, but also `text` for the font, `par` for line spacing, and `block` to control potential page breaks. A complete table of which EFs interact in which situation can be found **where??**. + +The set of active EFs changes frequently during the processing of your document. To provide consistent access to precisely the members of the active ensemble at a given point, typst uses the concept of a _context_ (see section [Context]($context) for detailed information). By default, typst expressions refer to the context "here" (i.e. to the moment when processing reaches the current document location), but you can specify and access many other processing stages as well, e.g. "whenever an equation will be rendered in the future" or "upon creation of the table-of-contents". By accessing element functions via contexts, it is guaranteed that parameters are manipulated in a coordinated way and in the appriate instances of the involved EFs. + +There are three fundamental methods to modify parameters: +- function calls, e.g. `#text(size: 25pt, [Hello]))` Use 25pt font to typeset just the given content, here 'Hello'. +- set rules, e.g. `#set text(size: 25pt)` Instruct the currently active `text` EF to use 25pt font until further notice. +- show rules, e.g. `#show math.equation: text.with(size: 25pt)` Typeset subsequent equations with 25pt font. + +Show rules are the most powerful (and most complicated), because they give you access to an entire active ensemble of EFs. Parameter modifications like the ones above have generally a _limited lifetime_: A modification passed as a function argument expires when the function completes, and the original parameter value is restored. Set and show rules are active until the end of the enclosing scope, i.e. until the next closing bracket or brace: + +```example +#set text(size: 11pt) // #1 +this is 11pt \ +#[ // open a new scope + still 11pt \ + #set text(size: 25pt) // #2 + now 25pt \ +] // end of scope, #2 expires +// #1 reactivated +again 11pt +``` + +When several rules refering to the same parameter occur in the same scope, each one overrides the previous specification. Modifications applied outside of any scope, i.e. at the top level of the document, remain active during the entire typesetting process unless they are explicitly overridden. These global settings are usually provided by a style template. + +Scopes give rise to a common pitfall when you want to apply rules conditionally. The following code has no effect: + +```typ +#if some-condition() { + set text(size: 11pt) // #1 +} else { // end of scope, #1 expires + set text(size: 25pt) // #2 +} // end of scope, #2 expires +// original behavior restored +``` + +You must use a postfix conditional instead + +```typ +#set text(size: 11pt) if some-condition() +#set text(size: 25pt) if not some-condition() +// no scope, modification is active +``` + +Now, let's delve into the details of set and show rules. + ## Set rules With set rules, you can customize the appearance of elements. They are written as a [function call]($function) to an [element From 5e08adafbaa2543b8da5cdb9431ad94a445566db Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 6 Mar 2025 10:37:44 +0100 Subject: [PATCH 2/5] Use term "set-if rule" --- docs/reference/language/styling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md index cbfff279e..68c40b0c3 100644 --- a/docs/reference/language/styling.md +++ b/docs/reference/language/styling.md @@ -50,7 +50,7 @@ Scopes give rise to a common pitfall when you want to apply rules conditionally. // original behavior restored ``` -You must use a postfix conditional instead +You must use the "set-if rule" instead ```typ #set text(size: 11pt) if some-condition() From 50fa0784514136e6c79e2b86d703ed413fb0f4ef Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 6 Mar 2025 17:45:14 +0100 Subject: [PATCH 3/5] Revised documentation of set and show rules --- docs/reference/language/styling.md | 281 ++++++++++++++++++++++------- 1 file changed, 216 insertions(+), 65 deletions(-) diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md index 68c40b0c3..7016a004b 100644 --- a/docs/reference/language/styling.md +++ b/docs/reference/language/styling.md @@ -12,18 +12,58 @@ of elements. ## The computational model behind typst -Typesetting is controlled by hundreds of parameters, from page margins to font sizes to numbering conventions. Managing this mess is a major focus of every typesetting system. The first step is to arrange these properties into related groups. In typst, these groups are called _element functions_ (EFs), for example `text` for font properties (typeface, size, color etc.), `par` for paragraphs (line spacing, alignment, indentation etc.), and `figure` (placement rules, captioning, and numbering of tables and figures). A complete list of the available element functions is **where??**. The term _functions_ indicates that these entities do not merely act as passive property containers, but can actively process content (usually provided in the function's `body` parameter) according to the present parameter settings. +Typesetting is controlled by hundreds of parameters, from page margins to font +sizes to numbering conventions. Managing this mess is a major focus of every +typesetting system. The first step is to arrange these properties into related +groups. In typst, these groups are called _element functions_ (EFs), for +example `text` for font properties (typeface, size, color etc.), `par` for +paragraphs (line spacing, alignment, indentation etc.), and `figure` +(placement rules, captioning, and numbering of tables and figures). A complete +list of the available element functions is **where??**. The term _functions_ +indicates that these entities do not merely act as passive property +containers, but can actively process content (provided in the function's +positional argument `body`) according to the present parameter settings. -Since different parts of the document need different settings for the same parameters (e.g. the font size in headings, plain text, and footnotes), each element function type exists in multiple instances. On the other hand, different EF types must interact to typeset a particular piece of content. You can imagine this as an _ensemble_ of EMs playing together to create the desired output. For example, math rendering requires the `math.equation` EF for equation-specific information, but also `text` for the font, `par` for line spacing, and `block` to control potential page breaks. A complete table of which EFs interact in which situation can be found **where??**. +Since different parts of the document need different settings for the same +parameters (e.g. the font size in headings, plain text, and footnotes), an +element function type usually exists in multiple instances. On the other hand, +different EF types must interact to typeset a particular piece of content. You +can imagine this as an _ensemble_ of EMs playing together to create the +desired output. For example, math rendering requires the `math.equation` EF +for equation-specific information, but also `text` for the font, `par` for +line spacing, and `block` to control potential page breaks. -The set of active EFs changes frequently during the processing of your document. To provide consistent access to precisely the members of the active ensemble at a given point, typst uses the concept of a _context_ (see section [Context]($context) for detailed information). By default, typst expressions refer to the context "here" (i.e. to the moment when processing reaches the current document location), but you can specify and access many other processing stages as well, e.g. "whenever an equation will be rendered in the future" or "upon creation of the table-of-contents". By accessing element functions via contexts, it is guaranteed that parameters are manipulated in a coordinated way and in the appriate instances of the involved EFs. +The set of active EFs changes frequently during the processing of your +document. To provide consistent access to precisely the members of the active +ensemble at a given point, typst uses the concept of a _context_ (see section +[Context]($context) for detailed information). By default, context expressions +refer to the context "here" (i.e. to the moment when processing reaches the +current document location), but you can specify and access many other +processing stages as well, e.g. "whenever an equation will be rendered in the +future" or "upon creation of the table-of-contents". Contexts guarantee that +parameters are accessed in a coordinated way and from the appropriate EF +instances. There are three fundamental methods to modify parameters: -- function calls, e.g. `#text(size: 25pt, [Hello]))` Use 25pt font to typeset just the given content, here 'Hello'. -- set rules, e.g. `#set text(size: 25pt)` Instruct the currently active `text` EF to use 25pt font until further notice. -- show rules, e.g. `#show math.equation: text.with(size: 25pt)` Typeset subsequent equations with 25pt font. +- function calls, e.g. `#text(size: 25pt, [Hello]))` Use 25pt font to typeset +just the given content, here 'Hello'. +- set rules, e.g. `#set text(size: 25pt)` Instruct the `text` EF to use 25pt +font until further notice. +- show rules, e.g. `#show math.equation: set text(size: 25pt)` Typeset +subsequent equations with 25pt font. -Show rules are the most powerful (and most complicated), because they give you access to an entire active ensemble of EFs. Parameter modifications like the ones above have generally a _limited lifetime_: A modification passed as a function argument expires when the function completes, and the original parameter value is restored. Set and show rules are active until the end of the enclosing scope, i.e. until the next closing bracket or brace: +Show rules are the most powerful (and most complicated), because they give you +access to the interacting ensemble of EFs +in a precisely specified situation (here: equation rendering due to +`math.equation`). Consistency in a show rule is guaranteed because a show rule +always defines a context automatically, without the need to type the `context` +keyword explicitly. + +Parameter modifications like the ones above have generally a _limited +lifetime_: A modification passed as a function argument expires when the +function completes, and the previous parameter value is restored at this +point. Set and show rules are active until the end of the enclosing scope, +i.e. until the next closing bracket or brace: ```example #set text(size: 11pt) // #1 @@ -37,67 +77,93 @@ this is 11pt \ again 11pt ``` -When several rules refering to the same parameter occur in the same scope, each one overrides the previous specification. Modifications applied outside of any scope, i.e. at the top level of the document, remain active during the entire typesetting process unless they are explicitly overridden. These global settings are usually provided by a style template. - -Scopes give rise to a common pitfall when you want to apply rules conditionally. The following code has no effect: - -```typ -#if some-condition() { - set text(size: 11pt) // #1 -} else { // end of scope, #1 expires - set text(size: 25pt) // #2 -} // end of scope, #2 expires -// original behavior restored -``` - -You must use the "set-if rule" instead - -```typ -#set text(size: 11pt) if some-condition() -#set text(size: 25pt) if not some-condition() -// no scope, modification is active -``` +When several rules refering to the same parameter occur in the same scope, +each one overrides the previous specification. Modifications applied outside +of any scope, i.e. at the top level of the document, remain active during the +entire typesetting process unless they are explicitly overridden. These global +settings are usually provided by a [style template]($making-a-template). Now, let's delve into the details of set and show rules. ## Set rules -With set rules, you can customize the appearance of elements. They are written -as a [function call]($function) to an [element -function]($function/#element-functions) preceded by the `{set}` keyword (or -`[#set]` in markup). Only optional parameters of that function can be provided -to the set rule. Refer to each function's documentation to see which parameters -are optional. In the example below, we use two set rules to change the -[font family]($text.font) and [heading numbering]($heading.numbering). + +Set rules offer the easiest way to customize the appearance of subsequent +elements. +Their basic syntax resembles a [function call]($function) to an [element +function]($function/#element-functions) preceded by the `[#set]` keyword (or +`{set}` in script mode) + +```typ +#set element-function(parameter-spec) // in markup mode +#{ + set element-function(parameter-spec) // in script mode +} +``` + +`element-function` specifies the EF you want to modify. The `parameter-spec` +is a sequence of named +parameters with their new values, as in an ordinary function call. The +supported parameter +names are the same as in the constructor of the respective EF, as described in +the +function's documentation. In the following example, we use two set rules to +change the heading's +[numbering style]($heading.numbering) and the text's [font +family]($text.font). ```example -#set heading(numbering: "I.") -#set text( - font: "New Computer Modern" -) +#set heading(numbering: "I.1") +#set text(font: "New Computer Modern") = Introduction With set rules, you can style your document. ``` -A top level set rule stays in effect until the end of the file. When nested -inside of a block, it is only in effect until the end of that block. With a -block, you can thus restrict the effect of a rule to a particular segment of -your document. Below, we use a content block to scope the list styling to one -particular list. +Note that you cannot pass positional arguments in a set rule – a set rule +is not really a function call, it just uses the same syntax for convenience. + +A set rule refers to all instances of the given EF type and stays in effect +until the end of the present scope. +In particular, a top level set rule stays in effect until the end of the file +unless explicitly +overridden by another set rule. To restrict a set rule's lifetime, you can +enclose it in a code +block, i.e. in brackets or braces. Then, the rule expires at the end of the +block, and the previous behavior is restored. +Below, we use a content block to apply the modified list styling only to the +list in brackets: ```example This list is affected: #[ #set list(marker: [--]) - Dash -] +] // end of block, set rule expires This one is not: - Bullet ``` -Sometimes, you'll want to apply a set rule conditionally. For this, you can use -a _set-if_ rule. +The lifetime restriction to the current scope is especially powerful when set +rules are used inside show rules (see next section): Since a show rule +implicitly defines a block, any set rule embedded there is only active within +the show rule's context and does not influence typesetting in other situations. + +On the other hand, the lifetime restriction causes a common pitfall when you +want to apply a set rule conditionally. `set text(red)` in the following code +has no effect, because it expires before it can influence anything: + +```typ +#let task(body, critical: false) = { + if critical { + set text(red) + } // end of block, set rule expires + // original behavior restored + [- #body] +} +``` + +To avoid this, you must write the condition in postfix notation via a _set-if_ rule: ```example #let task(body, critical: false) = { @@ -109,30 +175,115 @@ a _set-if_ rule. #task(critical: false)[Work deadline] ``` + ## Show rules -With show rules, you can deeply customize the look of a type of element. The -most basic form of show rule is a _show-set rule._ Such a rule is written as the -`{show}` keyword followed by a [selector], a colon and then a set rule. The most -basic form of selector is an [element function]($function/#element-functions). -This lets the set rule only apply to the selected element. In the example below, -headings become dark blue while all other text stays black. -```example -#show heading: set text(navy) +With show rules, you can deeply customize the typesetting process for +a given type of element. There are two variants of show rules: You +can specify the desired modifications by a set rule, which is simple +but of limited expressivity, or by a function, which is more involved +but unleashes the full range of customization options: -= This is navy-blue -But this stays black. +```typ +// in markup mode +#show selector-pattern: set-rule // set-rule variant +#show selector-pattern: function // function variant + +#{ // likewise in script mode + show selector-pattern: set-rule // set-rule variant + show selector-pattern: function // function variant +} ``` -With show-set rules you can mix and match properties from different functions to -achieve many different effects. But they still limit you to what is predefined -in Typst. For maximum flexibility, you can instead write a show rule that -defines how to format an element from scratch. To write such a show rule, -replace the set rule after the colon with an arbitrary [function]. This function -receives the element in question and can return arbitrary content. The available -[fields]($scripting/#fields) on the element passed to the function again match -the parameters of the respective element function. Below, we define a show rule -that formats headings for a fantasy encyclopedia. +The [selector] pattern specifies the situation where the desired +modifications shall apply. +The most common form of `selector-pattern` is an [element +function]($function/#element-functions) identifier. This means that the +right-hand side code (the set rule or function) is executed in the context of +the selected EF, and all modifications expire after completion of this code +– modifications in a show rule cannot influence other typesetting +situations. In the example below, the selector pattern refers +to `heading`, so headings are printed red, while all other text stays black: + +```example +#show heading: set text(red) + += First-level headings are red +== Second-level headings are also red +But plain text stays black. +``` + +You can refine the `selector-pattern` by means of the [where]($function.where) +function. The arguments of `where` are named parameters with values, and the +supported parameter names are the same as in the element function's constructor. +The selector pattern then restricts the show rule's scope to the EF instances +conforming to the given parameter settings. The following example changes +the color only for the first-level headings and leaves everything else unchanged: + +```example +#show heading.where(level: 1): set text(red) + += First-level headings are red +== Second-level headings remain black +Plain text stays black as well. +``` + +A complete list of supported selector patterns is provided below. + +To overcome the limitations of set rules, you use the `function` variant of +the show rule. In this variant, the right-hand side is the name of an +arbitrary [function] that accepts exactly one positional argument (it can have +arbitrarily many named arguments) and returns arbitrary content: + +```example +#let always-say-thank-you(it) = { + it + set text(green) + [thank you] +} + +#show heading: always-say-thank-you + += This heading is boring +``` + +The function's argument (conventionally called `it`) is the EF that matched +the left-hand side of the show rule. The function implements arbitrary +modifications (via embedded set and show rules or any other code) and then +forwards the content for further processing, returns entirely new content, or +a combination thereof. + +To support advanced customization, you can query the current values of the +parameter fields of the function's argument (`it.depth` in the example below). +Likewise, you can query the parameter values of other element functions in the +show rule's scope, for example the current `text.size`. This is possible +because a show rule implicitly defines a [Context]($context) to expose this +information to the user – outside of a context, these fields are not +accessible. + +```example +#let always-say-thank-you(it) = { + it + if it.depth == 1 { + set text(red, size: text.size * 150%) + [I don't care] + } else { + set text(green) + [thank you] + } +} + +#set heading(numbering: "1.1 ") +#show heading: always-say-thank-you += This heading is boring +== This one is better +``` + +In pratice, the function is usually implemented in-place as an unnamed function +(aka. "lambda expression") with the syntax `it => { implementation }` for an +implementation in script mode or `it => [ implementation ]` for an +implementation in markup mode. In this more involved example, we define a +show rule that formats headings for a fantasy encyclopedia: ```example #set heading(numbering: "(I)") From a1bf9f20b8c6839f86e657f39fae579b9f91b9dd Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 6 Mar 2025 18:07:52 +0100 Subject: [PATCH 4/5] minor style fixes --- docs/reference/language/styling.md | 34 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md index 7016a004b..131640def 100644 --- a/docs/reference/language/styling.md +++ b/docs/reference/language/styling.md @@ -124,15 +124,12 @@ Note that you cannot pass positional arguments in a set rule – a set rule is not really a function call, it just uses the same syntax for convenience. A set rule refers to all instances of the given EF type and stays in effect -until the end of the present scope. -In particular, a top level set rule stays in effect until the end of the file -unless explicitly -overridden by another set rule. To restrict a set rule's lifetime, you can -enclose it in a code -block, i.e. in brackets or braces. Then, the rule expires at the end of the -block, and the previous behavior is restored. -Below, we use a content block to apply the modified list styling only to the -list in brackets: +until the end of the present scope. In particular, a top level set rule +stays in effect until the end of the file unless explicitly overridden by +another set rule. To restrict a set rule's lifetime, you can enclose it in +a code block, i.e. in brackets or braces. Then, the rule expires at the end +of the block, and the previous behavior is restored. Below, we use a content +block to apply the modified list styling only to the list in brackets: ```example This list is affected: #[ @@ -195,7 +192,7 @@ but unleashes the full range of customization options: } ``` -The [selector] pattern specifies the situation where the desired +The [selector]-pattern specifies the situation where the desired modifications shall apply. The most common form of `selector-pattern` is an [element function]($function/#element-functions) identifier. This means that the @@ -230,10 +227,11 @@ Plain text stays black as well. A complete list of supported selector patterns is provided below. -To overcome the limitations of set rules, you use the `function` variant of -the show rule. In this variant, the right-hand side is the name of an -arbitrary [function] that accepts exactly one positional argument (it can have -arbitrarily many named arguments) and returns arbitrary content: +To overcome the limitations of set rules on the right-hand side, you use the +`function` variant of the show rule. In this variant, the right-hand side is +the name of an arbitrary [function] that accepts exactly one positional +argument (it can have additional named arguments) and returns arbitrary +content: ```example #let always-say-thank-you(it) = { @@ -243,12 +241,11 @@ arbitrarily many named arguments) and returns arbitrary content: } #show heading: always-say-thank-you - = This heading is boring ``` The function's argument (conventionally called `it`) is the EF that matched -the left-hand side of the show rule. The function implements arbitrary +the left-hand side of the show rule. The function implements the desired modifications (via embedded set and show rules or any other code) and then forwards the content for further processing, returns entirely new content, or a combination thereof. @@ -265,7 +262,8 @@ accessible. #let always-say-thank-you(it) = { it if it.depth == 1 { - set text(red, size: text.size * 150%) + set text(red, + size: text.size * 150%) [I don't care] } else { set text(green) @@ -279,7 +277,7 @@ accessible. == This one is better ``` -In pratice, the function is usually implemented in-place as an unnamed function +In practice, the function is usually implemented in-place as an unnamed function (aka. "lambda expression") with the syntax `it => { implementation }` for an implementation in script mode or `it => [ implementation ]` for an implementation in markup mode. In this more involved example, we define a From 8176cc9656265daabbb64c06a91ced0fa7e8625c Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Thu, 6 Mar 2025 18:18:03 +0100 Subject: [PATCH 5/5] Fixed broken link. --- docs/reference/language/styling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md index 131640def..ce26c6bfb 100644 --- a/docs/reference/language/styling.md +++ b/docs/reference/language/styling.md @@ -81,7 +81,7 @@ When several rules refering to the same parameter occur in the same scope, each one overrides the previous specification. Modifications applied outside of any scope, i.e. at the top level of the document, remain active during the entire typesetting process unless they are explicitly overridden. These global -settings are usually provided by a [style template]($making-a-template). +settings are usually provided by a [style template]($tutorial/making-a-template). Now, let's delve into the details of set and show rules.