Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heading with cross-reference only causes problem in various formats #12105

Open
CoryMcCartan opened this issue Feb 18, 2025 · 8 comments
Open
Assignees
Labels
bug Something isn't working crossref needs-discussion Issues that require a team-wide discussion before proceeding further
Milestone

Comments

@CoryMcCartan
Copy link

Bug description

When a section heading consists only of a cross-reference, a duplicate \label{} is created, leading to an incorrect number and hyperlink displaying in the PDF.

Steps to reproduce

---
format: 
    pdf:
        number-sections: true
---

## Section 1

::: {#prp-one}
It is the case that $1+1=2$.
:::

## Proofs of propositions

### @prp-one

Actual behavior

Image

Expected behavior

Image

Produced by replacing final line with ### [Proposition @prp-one]

Your environment

MacOS Sonoma 14.5

Quarto check output

Quarto 1.6.40
[✓] Checking environment information...
      Quarto cache location: /Users/cmccartan/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.4.0: OK
      Dart Sass version 1.70.0: OK
      Deno version 1.46.3: OK
      Typst version 0.11.0: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 1.6.40
      Path: /Applications/quarto/bin

[✓] Checking tools....................OK
      TinyTeX: (external install)
      Chromium: (not installed)

[✓] Checking LaTeX....................OK
      Using: TinyTex
      Path: /Users/cmccartan/Library/TinyTeX/bin/universal-darwin
      Version: 2024

[✓] Checking basic markdown render....OK

[✓] Checking Python 3 installation....OK
      Version: 3.9.6
      Path: /Library/Developer/CommandLineTools/usr/bin/python3
      Jupyter: (None)

      Jupyter is not available in this Python installation.
      Install with python3 -m pip install jupyter

[✓] Checking R installation...........OK
      Version: 4.4.0
      Path: /Library/Frameworks/R.framework/Resources
      LibPaths:
        - /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
      knitr: 1.49
      rmarkdown: 2.29

[✓] Checking Knitr engine render......OK
@CoryMcCartan CoryMcCartan added the bug Something isn't working label Feb 18, 2025
@mcanouil
Copy link
Collaborator

mcanouil commented Feb 18, 2025

That's not a bug or not really.

Sections automatically get an ID which are based on the sections' title.
In your case, it means ## @prp-one has prp-one as ID because @ is not valid for an ID, so you need to ensure you are not generating ID conflicts by specifying one explicitly.

InputOutput
---
format: pdf
number-sections: true
---

## Section 1

::: {#prp-one}
It is the case that $1+1=2$.
:::

@prp-one

## Proofs of propositions

### @prp-one {#proof-one}

@prp-one
Image

@mcanouil mcanouil added the latex LaTeX engines related libraries and technologies label Feb 18, 2025
@cderv
Copy link
Collaborator

cderv commented Feb 18, 2025

Thanks @mcanouil ! Definitely the problem.

I wonder if we can protect againt this somehow by detecting a referenced only label and tweaking the id on the header... 🤔

BTW, same kind of problem with HTML and Typst but less visible

  • HTML

     <section id="prp-one" class="level3" data-number="2.1">
     <h3 data-number="2.1" class="anchored" data-anchor-id="prp-one"><span class="header-section-number">2.1</span> <a href="#prp-one" class="quarto-xref">Proposition&nbsp;1</a></h3>
     </section>

    The id will be prp-one by default here, like the Markdown and not the output content.
    Setting an id explicitly is advised

  • Typst

    Typst is less forgiving and does not allow several label

     pandoc 
       to: typst
       output-file: index.typ
       standalone: true
       shift-heading-level-by: -1
       default-image-extension: svg
       wrap: none
       citeproc: false
       number-sections: true
       variables: {}
       
     metadata
       section-numbering: 1.1.a
     
     [typst]: Compiling index.typ to index.pdf...downloading @preview/ctheorems:1.1.0
       3.8 KiB /   3.8 KiB (100 %)   3.8 KiB/s in  0s ETA:  0s
     
     error: label `<prp-one>` occurs multiple times in the document
         ┌─ \\?\C:\Users\chris\Documents\DEV_OTHER\00-TESTS\test-quarto\index.typ:317:3317== @prp-one
         │    ^^^^^^^^
     
     ERROR: Error
         at Object.complete (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/output-typst.ts:79:13)
         at eventLoopTick (ext:core/01_core.js:175:7)
         at async file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render.ts:300:23
         at async withTimingAsync (file:///C:/Users/chris/DOCUME~1/DEV_R/QUARTO~1/src/core/timing.ts:47:20)
         at async Object.complete (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render.ts:276:7)
         at async Object.onPostProcess (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render-files.ts:720:28)
         at async renderFileInternal (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render-files.ts:689:3)
         at async renderFiles (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render-files.ts:325:9)
         at async renderProject (file:///C:/Users/chris/DOCUME~1/DEV_R/QUARTO~1/src/command/render/project.ts:464:23)
         at async renderForPreview (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/preview/preview.ts:428:24)

    Extract of typ file

     = Section 1
     <section-1>
     #proposition()[
     It is the case that $1 + 1 = 2$.
     
     ] <prp-one>
     = Proofs of propositions
     <proofs-of-propositions>
     == @prp-one
     <prp-one>

So I really thing we should try to find a clever way to help with that, or at least error early and ask to set an explicit ID.

@cderv cderv removed the latex LaTeX engines related libraries and technologies label Feb 18, 2025
@cderv cderv added this to the v1.7 milestone Feb 18, 2025
@cderv cderv changed the title Single cross-reference in section heading displays improperly in LaTeX Heading with cross-reference only causes problem in various formats Feb 18, 2025
@cderv cderv self-assigned this Feb 18, 2025
@cderv cderv added the crossref label Feb 18, 2025
@mcanouil
Copy link
Collaborator

mcanouil commented Feb 18, 2025

If Quarto does do something like adding -1 suffix, it should be explicitly told to the user, because it will be very difficult for a user to now figure out the ID to reference the section.

In my opinion, Quarto should not try to be smart here, instead it should warn or error out asking the user to use an explicit ID if the user really wants to proceed with this.

@CoryMcCartan
Copy link
Author

My 2¢ as a user:

  • If I had to pick between Quarto automatically adjusting so the cross-refs work versus always inserting consistent section IDs, I'd pick the former. I don't like to rely on auto IDs because if you change the section title, the cross-refs stop working.
  • I think auto IDs are less well-known than the general cross-ref syntax, so the latter producing (possibly) unexpected behavior because of the former seems un-ideal
  • That being said, maybe an error is the simplest way to go. Certainly would have saved me a few minutes looking around for the source of the problem.
  • No matter what, it would probably be good to add a description of the auto IDs for section headings on the cross-references page of the documentation at https://quarto.org/docs/authoring/cross-references.html#sections

Thanks both for your prompt replies.

@cderv
Copy link
Collaborator

cderv commented Feb 19, 2025

If Quarto does do something like adding -1 suffix, it should be explicitly told to the user, because it will be very difficult for a user to now figure out the ID to reference the section.

@mcanouil Do you think in current situation it is easier to guess ID ?

I don't think knowing the current ID used for the section is any easier in current situation as you can't guess that what you see in HTML as title, is not used as ID as usual.

From my experience, for simple header guessing ID is easy, but for header with special characters or punctuation, it is always tricky to remember what will be the normalization done by Pandoc.

So usually, if a user needs the id of a section, then adding an explicit one is recommended. And it is required if one wants to leverage crossreferencin for section: https://quarto.org/docs/authoring/cross-references.html#sections

IMO this leading to typst erroring is bad, so we need to make this better.

Rgarding easy guessing, we can also try to be clever and tweak the id based on the resulting cross-ref outputs. But IIRC the real problem in all that is Pandoc will use the initial ID no matter what to generate the TOC content. This can lead to problem...

Anyhow, this may not be trivial and for now throwing a warning or an error is maybe the only easy way.

No matter what, it would probably be good to add a description of the auto IDs for section headings on the cross-references page of the documentation at quarto.org/docs/authoring/cross-references.html#sections

@CoryMcCartan I am bit confused by your comments.

Cross-references sections with Quarto always require and explicit ID with prefix #sec-.

The auto ID feature comes from Pandoc with its extensions auto_identifiers that is default in most format.

This feature allows to reference to have an id set on all headings, so that adding a table of content works ok. They can also be use with the Markdown link syntax [Text](#heading-id)

So I don't think quarto.org/docs/authoring/cross-references.html#sections is the place to talk about automatic ID created. We could mention and link to Pandoc docs from Markdown basics on Headings: https://quarto.org/docs/authoring/markdown-basics.html#headings
But honestly it feels to advanced.

I think auto IDs are less well-known than the general cross-ref syntax, so the latter producing (possibly) unexpected behavior because of the former seems un-ideal

Yes exactly. The issue here is that two anchors are created as a side effect of the automatic ID feature from Pandoc with Quarto custom syntax for cross reference. IMO this is what we should solve by preventing this conflict. Either erroring early, or doing an automatic adjustment.

If I had to pick between Quarto automatically adjusting so the cross-refs work versus always inserting consistent section IDs, I'd pick the former. I don't like to rely on auto IDs because if you change the section title, the cross-refs stop working.

Do I understand correctly that your pick to have Quarto "fixing" the idea for you so it just works ?
The fact you said you don't like to rely on auto IDS make me doubt, as this would be like auto IDs, just Quarto making sure to avoid a duplicate anchor being created.


To sum up, this require experiment to really see what improvment could be done, while keeping in mind user experience.

Thanks for the discussion !! That really helps thinking process.

@cderv cderv modified the milestones: v1.7, Future Feb 19, 2025
@cderv cderv added the needs-discussion Issues that require a team-wide discussion before proceeding further label Feb 19, 2025
@cderv
Copy link
Collaborator

cderv commented Feb 19, 2025

BTW to illustrate @cscheid point in a recent discussion, the problem will be the same with something like this

## Section 1

::: {#prp-one}
It is the case that $1+1=2$.
:::

## Proofs of propositions

### PRP ONE

See @prp-one

because PRP ONE automatic id will be #prp-one

This is more complex to solve. This would require knowing all the heading id, and checking there is no overlap with cross referenced ids. Or something like that.

Possibly to solve this fully this a linting thing (warn about conflicted ids), or just completely opt out pandoc auto_identifiers feature and do our own stuff instead (e.g. for table of content)

@CoryMcCartan
Copy link
Author

@cderv apologies for the lack of clarity. The two points:

  • Documentation: the fact that Pandoc creates automatic IDs (that may conflict with explicitly written IDs) seems relevant to users relying on cross-references. Of course, if this issue is resolved in some other way, then maybe documentation isn't needed.

  • Yes, I prefer the automatic adjustment. The "cost" of such an adjustment is making it harder to guess the automatic ID. As you point out, this is already hard when the header contains special characters. More importantly, I don't think this is a common use pattern (it's mostly undocumented, anyway). An automatic adjustment makes things "just work" the way people will expect. If someone wants to link to a header in HTML with e.g. [Text](#heading-id), the current best-practice is to specify that heading ID manually; that wouldn't change. An explicit error in the case of conflicting IDs is of course workable too, though less user-friendly.

@cderv
Copy link
Collaborator

cderv commented Feb 20, 2025

the fact that Pandoc creates automatic IDs (that may conflict with explicitly written IDs) seems relevant to users relying on cross-references

Oh I see. We can definitely add a mention to this in crossref section 🤔

Yes, I prefer the automatic adjustment. The "cost" of such an adjustment is making it harder to guess the automatic ID. As you point out, this is already hard when the header contains special characters. More importantly, I don't think this is a common use pattern (it's mostly undocumented, anyway). An automatic adjustment makes things "just work" the way people will expect. If someone wants to link to a header in HTML with e.g. Text, the current best-practice is to specify that heading ID manually; that wouldn't change. An explicit error in the case of conflicting IDs is of course workable too, though less user-friendly.

I agree with you on this. Thanks for clarification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working crossref needs-discussion Issues that require a team-wide discussion before proceeding further
Projects
None yet
Development

No branches or pull requests

3 participants