How improve my code that count the number of sub-headings of the current section

I am trying to count how many sub-heading there is in the current section and display them in the heading.

= a

== 1
== 2
== 3
== 4

= b

== I
== II
== III

In the first heading (a), it should display a (sub-headers: 4), and for the second it should display b (sub-headers: 3).

#let sub-heading-counter = counter("sub-heading");

#show heading.where(level: 2): it => [
  #sub-heading-counter.step()
  #it
]

#show heading.where(level: 1): it => [
  #sub-heading-counter.update(0)
  #let sub-heading = {
    let next-headings = query(heading.where(level: 1).after(here()))
    if next-headings.len() > 1 {
      sub-heading-counter.at(next-headings.at(1).location())
    } else {
      sub-heading-counter.final()
    }.at(0)
  }
  #[#box[#it] (sub-headers: #sub-heading)]
]

The above does work, but:

  • It seems more complicated than needed
  • Is there a way to more the code inside the #show into a #let function? I was not able to understand what I had to do with context since it seems to be needed to use here() if I extract the code in a function.

This might be simpler?

#let n-sections() = {
  let later-chapters = query(heading.where(level: 1).after(here()))

  // Select sections after here…
  let target = heading.where(level: 2).after(here())
  // …and before the next chapter (if exists)
  if later-chapters.len() > 0 {
    let next-chapter = later-chapters.first()
    target = target.before(next-chapter.location())
  }

  query(target).len()
}

= a (sub-headers: #context n-sections())

== 1
== 2
== 3
== 4

= b (sub-headers: #context n-sections())

== I
== II
== III

here() needs context. You can define a function that retrieves information from the context, and provide the context to the function when calling it, like #context n-sections() above.

That’s perfect.

And if someone needs it, I did end up calling n-sections() like this:

#show heading.where(level: 1): it => block[
  #[#box[#it] (sub-headers: #context n-sections())]
]

Oh, well, you can use {…} to switch from markup mode to code mode to get rid of so many # and put newlines freely.

#show heading.where(level: 1): it => block({
  box(it)
  [ ]
  [(sub-headers: #context n-sections())]
})
2 Likes