From 52ea6ce44e9f427ad0ba0ac7ccc1b497edc4c5e2 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 11 May 2024 20:33:26 +0200 Subject: [PATCH 1/4] toctree, use document nesting instead of domain nesting --- sphinx/environment/collectors/toctree.py | 36 +++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py index 6ea148c631e..9c4a25c93b6 100644 --- a/sphinx/environment/collectors/toctree.py +++ b/sphinx/environment/collectors/toctree.py @@ -68,8 +68,6 @@ def build_toc( ) -> 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 @@ def build_toc( entries.append(onlynode) # check within the section for other node types elif isinstance(sectionnode, nodes.Element): + # cache of parents -> list item + memo_parents: dict[nodes.Element, nodes.Element] = {} toctreenode: nodes.Node for toctreenode in sectionnode.findall(): if isinstance(toctreenode, nodes.section): @@ -114,6 +114,10 @@ def build_toc( 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 @@ def build_toc( 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 - - entries.append(entry) + else: + assert parent == sectionnode + 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) From 24fc90a50ce4679b0965bca11b229ec36613cbfa Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:25:48 +0100 Subject: [PATCH 2/4] Add a test --- sphinx/environment/collectors/toctree.py | 4 +-- .../document_scoping.rst | 23 ++++++++++++ .../test-toctree-domain-objects/index.rst | 1 + .../test_environment_toctree.py | 35 +++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/roots/test-toctree-domain-objects/document_scoping.rst diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py index 9c4a25c93b6..dcf4ee178ef 100644 --- a/sphinx/environment/collectors/toctree.py +++ b/sphinx/environment/collectors/toctree.py @@ -101,8 +101,8 @@ def build_toc( entries.append(onlynode) # check within the section for other node types elif isinstance(sectionnode, nodes.Element): - # cache of parents -> list item - memo_parents: dict[nodes.Element, 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): diff --git a/tests/roots/test-toctree-domain-objects/document_scoping.rst b/tests/roots/test-toctree-domain-objects/document_scoping.rst new file mode 100644 index 00000000000..49aba9e4b11 --- /dev/null +++ b/tests/roots/test-toctree-domain-objects/document_scoping.rst @@ -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() diff --git a/tests/roots/test-toctree-domain-objects/index.rst b/tests/roots/test-toctree-domain-objects/index.rst index 77ee0100960..5f041725a78 100644 --- a/tests/roots/test-toctree-domain-objects/index.rst +++ b/tests/roots/test-toctree-domain-objects/index.rst @@ -4,3 +4,4 @@ :name: mastertoc domains + document_scoping diff --git a/tests/test_environment/test_environment_toctree.py b/tests/test_environment/test_environment_toctree.py index 175c6abe42b..04cb231c93c 100644 --- a/tests/test_environment/test_environment_toctree.py +++ b/tests/test_environment/test_environment_toctree.py @@ -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): From 2b92d3082c813322abda81fda63100b1b9c04833 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:33:44 +0100 Subject: [PATCH 3/4] fixup! Add a test --- tests/test_environment/test_environment_toctree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_environment/test_environment_toctree.py b/tests/test_environment/test_environment_toctree.py index 04cb231c93c..6979a129e47 100644 --- a/tests/test_environment/test_environment_toctree.py +++ b/tests/test_environment/test_environment_toctree.py @@ -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'} From 087ca262e6e941389d32b49cbf1c6e7bdb487b13 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 14 Jul 2024 01:00:40 +0100 Subject: [PATCH 4/4] CHANGES --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ac359fc8cba..fe35a5da613 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -145,6 +145,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 -------