singlehtml: Use same-document hyperlinks for internal project references (#12551)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
James Addison 2024-08-11 22:43:48 +01:00 committed by GitHub
parent a3f138329b
commit 0bfaadf6c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 45 additions and 2 deletions

View File

@ -54,6 +54,11 @@ Bugs fixed
get passed to :program:`latexmk`. Let :option:`-Q <sphinx-build -Q>`
(silent) apply as well to the PDF build phase.
Patch by Jean-François B.
* #11970, #12551: singlehtml builder: make target URIs to be same-document
references in the sense of :rfc:`RFC 3986, §4.4 <3986#section-4.4>`,
e.g., ``index.html#foo`` becomes ``#foo``.
(note: continuation of a partial fix added in Sphinx 7.3.0)
Patch by James Addison (with reference to prior work by Eric Norige)
Testing
-------

View File

@ -52,7 +52,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def fix_refuris(self, tree: Node) -> None:
# fix refuris with double anchor
fname = self.config.root_doc + self.out_suffix
for refnode in tree.findall(nodes.reference):
if 'refuri' not in refnode:
continue
@ -62,7 +61,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
continue
hashindex = refuri.find('#', hashindex + 1)
if hashindex >= 0:
refnode['refuri'] = fname + refuri[hashindex:]
# all references are on the same page...
refnode['refuri'] = refuri[hashindex:]
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
if isinstance(includehidden := kwargs.get('includehidden'), str):

View File

@ -2,6 +2,7 @@
import pytest
from tests.test_builders.xpath_html_util import _intradocument_hyperlink_check
from tests.test_builders.xpath_util import check_xpath
@ -78,6 +79,7 @@ def test_tocdepth(app, cached_etree_parse, fname, path, check, be_found):
(".//li[@class='toctree-l3']/a", '1.2.1. Foo B1', True),
(".//li[@class='toctree-l3']/a", '2.1.1. Bar A1', False),
(".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False),
(".//ul/li[@class='toctree-l1']/..//a", _intradocument_hyperlink_check),
# index.rst
('.//h1', 'test-tocdepth', True),
# foo.rst

View File

@ -4,6 +4,9 @@ import re
import pytest
from tests.test_builders.xpath_html_util import _intradocument_hyperlink_check
from tests.test_builders.xpath_util import check_xpath
@pytest.mark.sphinx(testroot='toctree-glob')
def test_relations(app):
@ -45,3 +48,17 @@ def test_numbered_toctree(app):
index = re.sub(':numbered:.*', ':numbered: 1', index)
(app.srcdir / 'index.rst').write_text(index, encoding='utf8')
app.build(force_all=True)
@pytest.mark.parametrize(
'expect',
[
# internal references should be same-document; external should not
(".//a[@class='reference internal']", _intradocument_hyperlink_check),
(".//a[@class='reference external']", r'https?://'),
],
)
@pytest.mark.sphinx('singlehtml', testroot='toctree')
def test_singlehtml_hyperlinks(app, cached_etree_parse, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect)

View File

@ -0,0 +1,19 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Sequence
from xml.etree.ElementTree import Element
def _intradocument_hyperlink_check(nodes: Sequence[Element]) -> None:
"""Confirm that a series of nodes are all HTML hyperlinks to the current page"""
assert nodes, 'Expected at least one node to check'
for node in nodes:
assert node.tag == 'a', 'Attempted to check hyperlink on a non-anchor element'
href = node.attrib.get('href')
# Allow Sphinx index and table hyperlinks to be non-same-document, as exceptions.
if href in {'genindex.html', 'py-modindex.html', 'search.html'}:
continue
assert not href or href.startswith('#'), 'Hyperlink failed same-document check'