How do I automatically label my headings, figures, equations?

With a large number of referenceable content, it may become bothersome to label and references elements manually. Automatically generating labels for content based on their body (headings), caption (figures) is an interesting approach but can also pollute your autocompletion field quickly.

Depending on your preferences, you may prefer manually labelling, having all content addressable, or some in-between solution.

The methods below are implemented for headings, but remain valid for figures. The only difference are the fields accessed. In the case of a figure, one may want to use the caption to generate the label text.

Approach 1: naive show rule (error)

#show heading: it => {
  let key = lower(it.body.text.replace(" ", "-"))
  [#it #label(key)]
}

= Long Heading with long title
See @long-heading-with-long-title.,

cf. Programmatically attaching labels to locatable elements #2926

As mentionned in the issue above, this simple approach results in an error: label <...> does not exist in the document.

As of 2024-09-17T00:00:00Z, it is still the case.

Approach 2: invisible figure workaround

Starting from the final result, you may obtain the following, where content is addressable even though no label is explictly defined.

= Test
@test

== Test 2 ah $x y z$ #rect(fill: black) test

@test-2-ah-x-y-z--test
Output

image

Complete code

You may simply copy the entire code below for your purposes.

Code
#let heading-numbering = "1.1"
#set heading(numbering: heading-numbering)
#let to-string(content) = {
  if content.has("text") and type(content.text) == "string" {
    content.text
  } else if content.has("children") {
    content.children.map(to-string).join("")
  } else if content.has("body") {
    to-string(content.body)
  } else if content == [ ] {
    " "
  }
}
#show heading: it => {
  let key = lower(to-string(it).replace(" ", "-"))
  return [
    #it
    #v(-1em)
    #figure(
      kind: "heading",
      numbering: (..numbers) => numbering(heading-numbering, ..(counter(heading).get())),
      supplement: "Section",
    )[]
    #label(key)
  ]
}

Technical details

This approach relies on:

  1. show heading rule adding an invisible, referenceable figure
  2. a to-string conversion function for the heading.body

numbering

In order to reference heading, you need to define the numbering[1] scheme first. As an example, "1.1" is enough.

#let heading-numbering = "1.1"
#set heading(numbering: heading-numbering)

to-string

The content to string function comes from GitHub issue #3876[2].

#let to-string(content) = {
  if content.has("text") and type(content.text) == "string" {
    content.text
  } else if content.has("children") {
    content.children.map(to-string).join("")
  } else if content.has("body") {
    to-string(content.body)
  } else if content == [ ] {
    " "
  }
}

show heading rule

In order:

  1. We generate the label key, replacing spaces by -
  2. Return the heading content by juxtaposing
    a. the original heading
    b. negative vertical space to remove the linebreak
    c. the figure used for labelling

Notice that the figure must folllow the heading-numbering scheme and the counter(heading).[3]

From this point, it is fairly simple to customize the generation rule for your own purposes, e.g., only generate level 1 heading labels, determine the heading supplement according to the level, etc.

#show heading: it => {
  let key = lower(to-string(it).replace(" ", "-"))
  return [
    #it
    #v(-1em)
    #figure(
      kind: "heading",
      numbering: (..numbers) => numbering(heading-numbering, ..(counter(heading).get())),
      supplement: "Section",
    )[]
    #label(key)
  ]
}

  1. Heading numbering ↩︎

  2. Turning content into string #3876
    ↩︎

  3. Introspection: counter ↩︎

3 Likes

Oh, neat feature. Here is a link about how date/time can be inserted: Insert Date / Time Question - #8 by david - Support - Discourse Meta. Now I can make my posts even better.

I can’t imagine wanting this over manually picking labels, but it’s interesting that it’s possible.

To be fair, I may or may not have too many headings … Also, most of the time, I end up writing the heading title, or the figure caption exactly as I did above, but manually. I just too the obvious next step, which was to automate it!