Skip to content

Commit c01af01

Browse files
authored
Merge pull request #950 from sphinx-contrib/restore-missing-anchor-links
Restore missing anchor links (autodocs and math)
2 parents 0dd2b99 + bffbcb3 commit c01af01

File tree

3 files changed

+100
-18
lines changed

3 files changed

+100
-18
lines changed

sphinxcontrib/confluencebuilder/storage/translator.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -297,15 +297,15 @@ def visit_paragraph(self, node):
297297

298298
self.body.append(self._start_tag(node, 'p', **attribs))
299299

300-
# For any names assigned to a paragraph, generate an anchor link to
301-
# ensure content can jump to this specific paragraph. This was
302-
# originally handled in `visit_target`, but now applied here since in
303-
# v2, anchors need to be inside paragraphs to prevent any undesired
304-
# extra spacing above the paragraph (before or after for v1, there is
305-
# no difference).
306-
if 'names' in node:
307-
for anchor in node['names']:
308-
self._build_anchor(node, anchor)
300+
# build anchors for ids which references may want to link to
301+
#
302+
# This was originally handled in `visit_target`, but moved into this
303+
# section since in v2, anchors need to be inside paragraphs to prevent
304+
# any undesired extra spacing above the paragraph (before or after for
305+
# v1, there is no difference). We also used to use `names` over `ids`
306+
# which worked for most things; however, some autodocs links seemed to
307+
# use some id-only targets instead.
308+
self._build_id_anchors(node)
309309

310310
self.context.append(self._end_tag(node, suffix=''))
311311

@@ -517,9 +517,7 @@ def visit_term(self, node):
517517
**{'style': f'margin-left: {offset}px;'}))
518518
self.context.append(self._end_tag(node))
519519

520-
if 'ids' in node:
521-
for id_ in node['ids']:
522-
self._build_anchor(node, id_)
520+
self._build_id_anchors(node)
523521

524522
if not self.v2:
525523
self.body.append(self._start_tag(node, 'dt'))
@@ -1431,9 +1429,7 @@ def visit_target(self, node):
14311429
# build an anchor link for them; example cases include documentation
14321430
# which generate a custom anchor link inside a paragraph
14331431
if 'ids' in node and 'refuri' not in node:
1434-
for id_ in node['ids']:
1435-
self._build_anchor(node, id_)
1436-
1432+
self._build_id_anchors(node)
14371433
self.body.append(self.encode(node.astext()))
14381434

14391435
raise nodes.SkipNode
@@ -1805,9 +1801,9 @@ def _visit_image(self, node, opts):
18051801
self.context.append(self._end_tag(node))
18061802

18071803
self.body.append(self._start_tag(node, 'ac:layout-cell'))
1808-
if 'ids' in node:
1809-
for id_ in node['ids']:
1810-
self._build_anchor(node, id_)
1804+
1805+
self._build_id_anchors(node)
1806+
18111807
self.body.append(self._end_tag(node))
18121808

18131809
self.body.append(self._start_tag(node, 'ac:layout-cell'))
@@ -3093,6 +3089,28 @@ def visit_raw(self, node):
30933089
# # #
30943090
# ##########################################################################
30953091

3092+
def _build_id_anchors(self, node):
3093+
"""
3094+
build anchors for a node that has any ids
3095+
3096+
A node may define one more more identifiers through an `ids` list.
3097+
For some nodes, it may be ideal to generate anchor links for each
3098+
identifier listed, which may be used by other nodes to link to. For
3099+
example, a paragraph may have multiple identifiers assigned to it,
3100+
which features like autotools may want to jump to said paragraph.
3101+
3102+
Note all use cases where a node has an identifier will need to build
3103+
an anchor (so, it is not done automatically). This call can be used
3104+
to explicitly build anchors on nodes that require it.
3105+
3106+
Args:
3107+
node: the node to generate anchors on
3108+
"""
3109+
3110+
if 'ids' in node:
3111+
for id_ in node['ids']:
3112+
self._build_anchor(node, id_)
3113+
30963114
def _build_anchor(self, node, anchor):
30973115
"""
30983116
build an anchor on a page
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. https://docutils.sourceforge.io/docs/ref/rst/directives.html#math
2+
3+
math
4+
----
5+
6+
.. math:: e^{i\pi} + 1 = 0
7+
:label: euler

tests/unit-tests/test_rst_math.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
# Copyright Sphinx Confluence Builder Contributors (AUTHORS)
3+
4+
from tests.lib.parse import parse
5+
from tests.lib.testcase import ConfluenceTestCase
6+
from tests.lib.testcase import setup_builder
7+
from tests.lib.testcase import setup_editor
8+
import shutil
9+
import unittest
10+
11+
12+
class TestConfluenceRstMath(ConfluenceTestCase):
13+
@classmethod
14+
def setUpClass(cls):
15+
# math images need latex to create
16+
if not shutil.which('latex'):
17+
msg = 'latex not installed'
18+
raise unittest.SkipTest(msg)
19+
20+
super().setUpClass()
21+
22+
cls.dataset = cls.datasets / 'rst' / 'math'
23+
24+
@setup_builder('confluence')
25+
def test_storage_rst_math_v1_default(self):
26+
out_dir = self.build(self.dataset)
27+
28+
with parse('index', out_dir) as data:
29+
images = data.find_all('ac:image')
30+
self.assertIsNotNone(images)
31+
self.assertEqual(len(images), 1)
32+
33+
# sanity check anchor creation
34+
anchor_tag = data.find('ac:structured-macro',
35+
{'ac:name': 'anchor'})
36+
self.assertIsNotNone(anchor_tag)
37+
anchor_param = anchor_tag.find('ac:parameter')
38+
self.assertIsNotNone(anchor_param)
39+
self.assertEqual(anchor_param.text, 'equation-euler')
40+
41+
@setup_builder('confluence')
42+
@setup_editor('v2')
43+
def test_storage_rst_math_v2_default(self):
44+
out_dir = self.build(self.dataset)
45+
46+
with parse('index', out_dir) as data:
47+
images = data.find_all('ac:image')
48+
self.assertIsNotNone(images)
49+
self.assertEqual(len(images), 1)
50+
51+
# sanity check anchor creation
52+
anchor_tag = data.find('ac:structured-macro',
53+
{'ac:name': 'anchor'})
54+
self.assertIsNotNone(anchor_tag)
55+
anchor_param = anchor_tag.find('ac:parameter')
56+
self.assertIsNotNone(anchor_param)
57+
self.assertEqual(anchor_param.text, 'equation-euler')

0 commit comments

Comments
 (0)