mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
toctree: Use document nesting instead of domain nesting when adding domain objects (#12367)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
159c26715b
commit
e08f12f848
@@ -149,6 +149,11 @@ Bugs fixed
|
||||
Patch by James Addison and Will Lachance.
|
||||
* #9634: Do not add a fallback language by stripping the country code.
|
||||
Patch by Alvin Wong.
|
||||
* #12352: Add domain objects to the table of contents
|
||||
in the same order as defined in the document.
|
||||
Previously, each domain used language-specific nesting rules,
|
||||
which removed control from document authors.
|
||||
Patch by Jakob Lykke Andersen and Adam Turner.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
@@ -68,8 +68,6 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
) -> nodes.bullet_list | None:
|
||||
# list of table of contents entries
|
||||
entries: list[Element] = []
|
||||
# cache of parents -> list item
|
||||
memo_parents: dict[tuple[str, ...], nodes.list_item] = {}
|
||||
for sectionnode in node:
|
||||
# find all toctree nodes in this section and add them
|
||||
# to the toc (just copying the toctree node which is then
|
||||
@@ -103,6 +101,8 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
entries.append(onlynode)
|
||||
# check within the section for other node types
|
||||
elif isinstance(sectionnode, nodes.Element):
|
||||
# cache of parent node -> list item
|
||||
memo_parents: dict[nodes.Element, nodes.list_item] = {}
|
||||
toctreenode: nodes.Node
|
||||
for toctreenode in sectionnode.findall():
|
||||
if isinstance(toctreenode, nodes.section):
|
||||
@@ -114,6 +114,10 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
note_toctree(app.env, docname, toctreenode)
|
||||
# add object signatures within a section to the ToC
|
||||
elif isinstance(toctreenode, addnodes.desc):
|
||||
# The desc has one or more nested desc_signature,
|
||||
# and then a desc_content, which again may have desc nodes.
|
||||
# Thus, desc is the one we can bubble up to through parents.
|
||||
entry: nodes.list_item | None = None
|
||||
for sig_node in toctreenode:
|
||||
if not isinstance(sig_node, addnodes.desc_signature):
|
||||
continue
|
||||
@@ -136,22 +140,28 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
para = addnodes.compact_paragraph('', '', reference,
|
||||
skip_section_number=True)
|
||||
entry = nodes.list_item('', para)
|
||||
*parents, _ = sig_node['_toc_parts']
|
||||
parents = tuple(parents)
|
||||
|
||||
# Cache parents tuple
|
||||
memo_parents[sig_node['_toc_parts']] = entry
|
||||
|
||||
# Nest children within parents
|
||||
if parents and parents in memo_parents:
|
||||
root_entry = memo_parents[parents]
|
||||
# Find parent node
|
||||
parent = sig_node.parent
|
||||
while parent not in memo_parents and parent != sectionnode:
|
||||
parent = parent.parent
|
||||
# Note, it may both be the limit and in memo_parents,
|
||||
# prefer memo_parents, so we get the nesting.
|
||||
if parent in memo_parents:
|
||||
root_entry = memo_parents[parent]
|
||||
if isinstance(root_entry[-1], nodes.bullet_list):
|
||||
root_entry[-1].append(entry)
|
||||
else:
|
||||
root_entry.append(nodes.bullet_list('', entry))
|
||||
continue
|
||||
else:
|
||||
assert parent == sectionnode
|
||||
entries.append(entry)
|
||||
|
||||
entries.append(entry)
|
||||
# Save the latest desc_signature as the one we put sub entries in.
|
||||
# If there are multiple signatures, then the latest is used.
|
||||
if entry is not None:
|
||||
# are there any desc nodes without desc_signature nodes?
|
||||
memo_parents[toctreenode] = entry
|
||||
|
||||
if entries:
|
||||
return nodes.bullet_list('', *entries)
|
||||
|
23
tests/roots/test-toctree-domain-objects/document_scoping.rst
Normal file
23
tests/roots/test-toctree-domain-objects/document_scoping.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
Level 1
|
||||
=======
|
||||
|
||||
.. py:class:: ClassLevel1a
|
||||
ClassLevel1b
|
||||
|
||||
.. py:method:: f()
|
||||
|
||||
.. py:method:: ClassLevel1a.g()
|
||||
|
||||
.. py:method:: ClassLevel1b.g()
|
||||
|
||||
Level 2
|
||||
-------
|
||||
|
||||
.. py:class:: ClassLevel2a
|
||||
ClassLevel2b
|
||||
|
||||
.. py:method:: f()
|
||||
|
||||
.. py:method:: ClassLevel2a.g()
|
||||
|
||||
.. py:method:: ClassLevel2b.g()
|
@@ -4,3 +4,4 @@
|
||||
:name: mastertoc
|
||||
|
||||
domains
|
||||
document_scoping
|
||||
|
@@ -132,7 +132,7 @@ def test_domain_objects(app):
|
||||
|
||||
assert app.env.toc_num_entries['index'] == 0
|
||||
assert app.env.toc_num_entries['domains'] == 9
|
||||
assert app.env.toctree_includes['index'] == ['domains']
|
||||
assert app.env.toctree_includes['index'] == ['domains', 'document_scoping']
|
||||
assert 'index' in app.env.files_to_rebuild['domains']
|
||||
assert app.env.glob_toctrees == set()
|
||||
assert app.env.numbered_toctrees == {'index'}
|
||||
@@ -161,6 +161,41 @@ def test_domain_objects(app):
|
||||
[list_item, ([compact_paragraph, reference, literal, "HelloWorldPrinter.print()"])])
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='toctree-domain-objects')
|
||||
def test_domain_objects_document_scoping(app):
|
||||
app.build()
|
||||
|
||||
# tocs
|
||||
toctree = app.env.tocs['document_scoping']
|
||||
assert_node(
|
||||
toctree,
|
||||
[bullet_list, list_item, (
|
||||
compact_paragraph, # [0][0]
|
||||
[bullet_list, ( # [0][1]
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel1a'], # [0][1][0]
|
||||
[list_item, ( # [0][1][1]
|
||||
[compact_paragraph, reference, literal, 'ClassLevel1b'], # [0][1][1][0]
|
||||
[bullet_list, list_item, compact_paragraph, reference, literal, 'ClassLevel1b.f()'], # [0][1][1][1][0]
|
||||
)],
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel1a.g()'], # [0][1][2]
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel1b.g()'], # [0][1][3]
|
||||
[list_item, ( # [0][1][4]
|
||||
[compact_paragraph, reference, 'Level 2'], # [0][1][4][0]
|
||||
[bullet_list, ( # [0][1][4][1]
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel2a'], # [0][1][4][1][0]
|
||||
[list_item, ( # [0][1][4][1][1]
|
||||
[compact_paragraph, reference, literal, 'ClassLevel2b'], # [0][1][4][1][1][0]
|
||||
[bullet_list, list_item, compact_paragraph, reference, literal, 'ClassLevel2b.f()'], # [0][1][4][1][1][1][0]
|
||||
)],
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel2a.g()'], # [0][1][4][1][2]
|
||||
[list_item, compact_paragraph, reference, literal, 'ClassLevel2b.g()'], # [0][1][4][1][3]
|
||||
)],
|
||||
)],
|
||||
)],
|
||||
)],
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('xml', testroot='toctree')
|
||||
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
|
||||
def test_document_toc(app):
|
||||
|
Reference in New Issue
Block a user