diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index 3079c7dc543..c5c1b05f089 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -464,16 +464,11 @@ def _toctree_add_classes(node: Element, depth: int, docname: str) -> None:
ET = TypeVar('ET', bound=Element)
-
-def _toctree_copy(
- node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tags
-) -> ET:
- """Utility: Cut and deep-copy a TOC at a specified depth."""
- keep_bullet_list_sub_nodes = depth <= 1 or (
- (depth <= maxdepth or maxdepth <= 0) and (not collapse or 'iscurrent' in node)
- )
-
- copy = node.copy()
+def _toctree_copy_children(
+ copy: ET, node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tags
+) -> None:
+ """Utility: used to copy node children except 'only' nodes which are not
+ toctree nodes but conditions to include or not their children."""
for subnode in node.children:
if isinstance(subnode, addnodes.compact_paragraph | nodes.list_item):
# for
and
, just recurse
@@ -489,18 +484,11 @@ def _toctree_copy(
# copy sub toctree nodes for later processing
copy.append(subnode.copy())
elif isinstance(subnode, addnodes.only):
- # only keep children if the only node matches the tags
+ # Only keep its children if the 'only' node matches the tags. When
+ # matching, call recursively the current function without
+ # incrementing the depth as 'only' nodes are not toctree nodes.
if _only_node_keep_children(subnode, tags):
- for child in subnode.children:
- copy.append(
- _toctree_copy(
- child,
- depth,
- maxdepth,
- collapse,
- tags, # type: ignore[type-var]
- )
- )
+ _toctree_copy_children(copy, subnode, depth, maxdepth, collapse, tags)
elif isinstance(subnode, nodes.reference | nodes.title):
# deep copy references and captions
sub_node_copy = subnode.copy()
@@ -511,6 +499,19 @@ def _toctree_copy(
else:
msg = f'Unexpected node type {subnode.__class__.__name__!r}!'
raise ValueError(msg)
+
+ return copy
+
+def _toctree_copy(
+ node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tags
+) -> ET:
+ """Utility: Cut and deep-copy a TOC at a specified depth."""
+ keep_bullet_list_sub_nodes = depth <= 1 or (
+ (depth <= maxdepth or maxdepth <= 0) and (not collapse or 'iscurrent' in node)
+ )
+
+ copy = node.copy()
+ _toctree_copy_children(copy, node, depth, maxdepth, collapse, tags)
return copy