#import "../../../components/index.typ": docs-chapter

#show: docs-chapter.with(
  title: "Context",
  route: "/reference/context",
  description: "How to deal with content that reacts to its location in the document.",
)

Sometimes, we want to create content that reacts to its location in the document. This could be a localized phrase that depends on the configured text language or something as simple as a heading number which prints the right value based on how many headings came before it. However, Typst code isn't directly aware of its location in the document. Some code at the beginning of the source text could yield content that ends up at the back of the document.

To produce content that is reactive to its surroundings, we must thus specifically instruct Typst: We do this with the `{context}` keyword, which precedes an expression and ensures that it is computed with knowledge of its environment. In return, the context expression itself ends up opaque. We cannot directly access whatever results from it in our code, precisely because it is contextual: There is no one correct result, there may be multiple results in different places of the document. For this reason, everything that depends on the contextual data must happen inside of the context expression.

Aside from explicit context expressions, context is also established implicitly in some places that are also aware of their location in the document: @reference:styling:show-rules[Show rules] provide context #footnote[Currently, all show rules provide @reference:context:style-context[style context], but only show rules on @location:locatable[locatable] elements provide a @reference:context:location-context[location context].] and numberings in the outline, for instance, also provide the proper context to resolve counters.

= Style context <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.

```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.

```example
#let value = context text.lang
#value

#set text(lang: "de")
#value

#set text(lang: "fr")
#value
```

Crucially, upon creation, `value` becomes opaque @content[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.

= Location context <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 to other elements, and absolutely on the pages. We can use this information to create very flexible interactions between different document parts. This underpins features like heading numbering, the table of contents, or page headers dependent on section headings.

Some functions like @counter.get implicitly access the current location. In the example below, we want to retrieve the value of the heading counter. Since it changes throughout the document, we need to first enter a context expression. Then, we use `get` to retrieve the counter's current value. This function accesses the current location from the context to resolve the counter value. Counters have multiple levels and `get` returns an array with the resolved numbers. Thus, we get the following result:

```example
#set heading(numbering: "1.")

= Introduction
#lorem(5)

#context counter(heading).get()

= Background
#lorem(5)

#context counter(heading).get()
```

For more flexibility, we can also use the @here function to directly extract the current @location[location] from the context. The example below demonstrates this:

- We first have `{counter(heading).get()}`, which resolves to `{(2,)}` as before.
- We then use the more powerful  @counter.at with @here, which in combination is equivalent to `get`, and thus get `{(2,)}`.
- Finally, we use `at` with a @label[label] to retrieve the value of the counter at a _different_ location in the document, in our case that of the introduction heading. This yields `{(1,)}`. Typst's context system gives us time travel abilities and lets us retrieve the values of any counters and states at _any_ location in the document.

```example
#set heading(numbering: "1.")

= Introduction <intro>
#lorem(5)

= Background <back>
#lorem(5)

#context [
  #counter(heading).get() \
  #counter(heading).at(here()) \
  #counter(heading).at(<intro>)
]
```

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[selector] that resolves to a unique element (could also be a label) and returns the position on the pages for that element.

```example
Background is at: \
#context locate(<back>).position()

= Introduction <intro>
#lorem(5)
#pagebreak()

= Background <back>
#lorem(5)
```

There are other functions that make use of the location context, most prominently @query. Take a look at the @reference:introspection[introspection] category for more details on those.

= Nested contexts <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 @length.to-absolute[`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 <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 placeholder position, layouts your document and then recompiles with the known position from the finished layout. The same approach is taken to resolve counters, states, and queries. In certain cases, Typst may even need more than two iterations to resolve everything. While that's sometimes a necessity, it may also be a sign of misuse of contextual functions (e.g. of @state:caution[state]). If Typst cannot resolve everything within five attempts, it will stop and output the warning "document did not converge within five attempts."

A very careful reader might have noticed that not all of the functions presented above actually make use of the current location. While `{counter(heading).get()}` definitely depends on it, `{counter(heading).at(<intro>)}`, for instance, does not. However, it still requires context. While its value is always the same _within_ one compilation iteration, it may change over the course of multiple compiler iterations. If one could call it directly at the top level of a module, the whole module and its exports could change over the course of multiple compiler iterations, which would not be desirable.
