How do I calculate the width of content without having context everywhere?

As far as I understand, that is largely not an issue anymore since 0.12. Back with 0.11, there were cases where making context smaller was important, although I’m not sure what the implications were.

In any case, even then you’d have had to make the context big enough and what you’re doing here is already the smallest context scope possible (at least as long that we assume that there’s no way around measuring inside the diagram). And even though it’s quite a bit of code, I’d say one diagram is actually not that big of a scope anyway.

That said, what are the implications of using a large scope? One important one is that the context is “frozen” in the whole scope. Consider this:

#context {
  [#text.size]
  set text(size: 2em)
  [#text.size]
}

both of these will print the same value, but in different font sizes! You access text.size contextually, and the value you access is the one active at the beginning of the context block. One way around this is to break this into two smaller context blocks, but even if that is not possible, you can work around this by nesting context blocks:

#context {
  [#text.size]
  set text(size: 2em)
  context [#text.size]
}

Another implication is that context blocks will sometimes fail in early iterations. Typst will retry when the context changes, but if the source and consumer of that change are in the same block, this won’t work. The workaround is the same:

#let x = state("x")
#context {
  x.update(1)

  // bad: the context fails and the update isn't applied,
  // leading to the context to continue failing
  // assert.eq(x.get(), 1)

  // good: only this context fails, so the update is applied
  // on the next attempt, this succeeds
  context assert.eq(x.get(), 1)
}

You can read more about this behavior here: Is an assert supposed to be able to change behavior when it "doesn't" fail? - #2 by laurmaedje and in the issue linked there.

2 Likes