Skip to content

Conversation

gastmaier
Copy link

Purpose

Follow up to #13717, inverting the logic, instead of patching the toctree to yield "#id1" instead of "#document-path/to#id1", have the section id to be docname preffixed, solving non-unique ids in singlehtml.
Allows to remove post Sphinx transforms like in here

Top level overview of current behavior

  • ID collision is resolved per doc (#already-used -> #id1, #already-used -> #id2).
  • There is no ID collision resolution on singlehtml step.

Approach taken

Based on the LaTeX builder solution.
sphinx/writers/latex.py#hypertarget[withdoc=True] method suffixes docutils id with the docname.
In my implementation I edit ids['0'] directly to not have to overwrite the whole visit_section method, but I understand if requested to not modify the tree and instead overwrite.

On the format #document-test/extra#id1

It is compatible with HTML anchoring, CSS and JavaScript selectors, but require escaping:

#document-test\/extra\#test {color: #f00;}
document.querySelector('#document-test\\/extra\\#test')

Tests

The following tests are relevant:

  • tests/test_builders/test_build_html_tocdepth.py
  • test_build_html_numfig.py

References

@gastmaier gastmaier force-pushed the toctree-singlehtml2 branch 3 times, most recently from e6b65fb to 5117057 Compare July 20, 2025 14:23
@gastmaier gastmaier marked this pull request as ready for review July 21, 2025 07:39
@AA-Turner AA-Turner added the sprint For work completed at a conference or similar event. label Jul 21, 2025
@jayaddison
Copy link
Contributor

Hi @gastmaier - I'm a former semi-regular volunteer contributor here, although I have been less active recently. Thanks for the pull request; and sorry that I did not notice the toctree constructor problem, as you mention in #13717.

I am reading both #13717 and this PR #13739 to try to understand the different approaches and reasons for them.

Also: do you have a test case that we could add under tests/roots that demonstrates the problem? I suppose it would need to include a table of contents of some kind and have a corresponding singlehtml test case.

@gastmaier
Copy link
Author

Hi @jayaddison maybe extending tests/test_builders/test_build_html_tocdepth.py
to check for duplicated ids?
as is, it already checks if ids are as expected (e.g., the pr changes things in 3 location to keep passing the test)., but not for duplicated ids, so I guess I can add that assertion

@jayaddison
Copy link
Contributor

@gastmaier that sounds perfect, yep! (I'd forgotten about those tests)

Copy link

@akhilsmokie7-cloud akhilsmokie7-cloud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align with purpose

To assert unique ids in singlehtml builder.

Signed-off-by: Jorge Marques <[email protected]>
@gastmaier gastmaier force-pushed the toctree-singlehtml2 branch 2 times, most recently from c50ba56 to 910de47 Compare August 30, 2025 17:32
@gastmaier
Copy link
Author

gastmaier commented Aug 30, 2025

Hi, @jayaddison and @akhilsmokie7-cloud I rebased and added the test to check for duplicated ids.
This pr relies on changing the ids during the write step.
Initially I didn't really like the approach, but I recently stumble on the fact that the html build also changes the images src path during the write step (original/path/to/image.png -> _images/image_<counter>.png), so I am now more comfortable with this approach.

I added the test to the bottom, checking out fe728f4 will fail at

FAILED tests/test_builders/test_build_html_tocdepth.py::test_unique_ids_singlehtml - AssertionError: assert 16 == 15

as expected, since at f5457f1
I purposely added a section called FooBar to both foo bar, forcing the same id in both pages, which is a problem only for single output.

On, "the html build also changes the images src path during the write step", this is what I am talking about
https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/html5.py#L754-L755

CI note:
Failing test

FAILED tests/test_directives/test_directive_only.py::test_sectioning - AssertionError: Section out of place: '1.6.2. Subsection'
assert '1.6.1.1.' == '1.6.2.'

is due to 2e51b787680cefdfe56b3438d809e6476600a47e

Thanks,

Since the singlehtml aggregates all doc files into a single html page
during the write step, and the ids must be unique for proper link
anchoring, add test that collects all ids in the page and checks if all
ids are unique, by asserting the length of the list against it as a set.
And use modified id directly, modified in the previous commit to ensure
unique id in singlehtml.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sprint For work completed at a conference or similar event.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants