How to automatically add * before headings?

#set heading(numbering: "1.1")
#show heading: set heading()
= 有理数
== 正数和负数
== 正数和负数

image

There are two options here, I will leave it up to you to select the one that best suits your requirements.

You can use a show rule for the headings to insert the asterisk between the numbering and body of each heading. You can adjust the spacing around the asterisk with spaces inside the string or with the horizontal spacing function h().
Edit: Manually recreating the counter value with numbering() and the heading location is unnecessary, see the reply by @Andrew. You can just use counter(heading).display() to get the correct output for the current heading.

#show heading: it => block(counter(heading).display() + "*" + it.body)

The second option is to include the asterisk in the numbering. Compared to the first option the asterisk will then be automatically included wherever the heading numbering shows up, e.g. also in references to headings or in the outline.

#let asterisk-numbering(..n) = numbering("1.1", ..n) + "*"
#set heading(numbering: asterisk-numbering)

How to post in the Questions category :

Why overcomplicate?

#set heading(numbering: "1.1")
#show heading: it => block[#counter(heading).display()#h(0.3em)\*#it.body]

This is, if not all headings features are needed. Actual heading show rule is very big.

1 Like

I don’t understand this very well

See Add default typst implementation code where possible · Issue #5095 · typst/typst · GitHub for full heading re-implementation in Typst. I guess, basically, it doesn’t respond to overridden numbering and ignore hanging-indent (and text.dir). And no HTML support.

// #set heading(numbering: "1.1")
#show heading: it => {
  if it.body.has("text") and it.body.text.first() != "*" {
    heading(
      "*" + it.body, 
      level: it.level,
      numbering: "1.1", 
    )
  } else {
    it
  }
}
= 有理数
== 正数和负数
== 正数和负数

If I don’t comment out this line #set heading(numbering: "1.1") , the result won’t be as expected. However, I don’t understand how it works internally—I can only look at the Rust source code to figure it out. Why isn’t this heading outline functionality provided as part of the standard library? I’m not sure what Typst’s reasoning is for this design.

@Andrew @janekfleper

You are creating every heading twice with your current show rule. If you hover over it.body in the if statement, you can see that it shows every section twice. Once with the initial body and once with "*" + body. With the set rule for the heading numbering, you are then incrementing the heading counter twice for each heading. Without the set rule only the inner heading has a numbering and the heading counter is only incremented once. The outer heading will have numbering set to none which does not increment the counter.

Not sure how this can be a solution if you are saying that it doesn’t really work. Here is a proper solution:

#set heading(numbering: "1.1")
#show heading: it => {
  if it.body.func() != text and it.body.children.first() == [\*] { return it }
  let number = if it.numbering != none [#counter(heading).display()#h(0.3em)]
  block[#number\*#it.body]
}

= 有理数
== \*正数和负数
== 正数和负数

image

It works with and without the set rule. But you should only use = syntax. Otherwise, the solution will be slightly longer.

I would just not add an asterisk in the heading in the first place. Then How to automatically add * before headings? - #4 by Andrew just works.