Skip to content

Commit 164997c

Browse files
committed
Prune the new node in _resolve_toctree instead of copying, to accelerate the call.
1 parent cc7c6f4 commit 164997c

File tree

1 file changed

+70
-1
lines changed

1 file changed

+70
-1
lines changed

sphinx/environment/adapters/toctree.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def _resolve_toctree(
204204

205205
# prune the tree to maxdepth, also set toc depth and current classes
206206
_toctree_add_classes(newnode, 1, docname)
207-
newnode = _toctree_copy(newnode, 1, maxdepth if prune else 0, collapse, tags)
207+
_toctree_prune(newnode, 1, maxdepth if prune else 0, collapse, tags)
208208

209209
if (
210210
isinstance(newnode[-1], nodes.Element) and len(newnode[-1]) == 0
@@ -559,6 +559,75 @@ def _toctree_copy_seq(
559559
raise ValueError(msg)
560560

561561

562+
def _toctree_prune[ET: Element](
563+
node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tags
564+
):
565+
"""Utility: Cut and deep-copy a TOC at a specified depth."""
566+
assert not isinstance(node, addnodes.only)
567+
depth = max(depth - 1, 1)
568+
_toctree_prune_seq(node, depth, maxdepth, collapse, tags, initial_call=True)
569+
570+
571+
def _toctree_prune_seq(
572+
node: Node,
573+
depth: int,
574+
maxdepth: int,
575+
collapse: bool,
576+
tags: Tags,
577+
*,
578+
initial_call: bool = False,
579+
is_current: bool = False,
580+
):
581+
node: Element
582+
583+
if isinstance(node, (addnodes.compact_paragraph, nodes.list_item)):
584+
# for <p> and <li>, just recurse
585+
for subnode in node.children:
586+
_toctree_prune_seq( # type: ignore[assignment,operator]
587+
subnode, depth, maxdepth, collapse, tags, is_current='iscurrent' in node
588+
)
589+
return
590+
591+
if isinstance(node, nodes.bullet_list):
592+
# for <ul>, copy if the entry is top-level
593+
# or, copy if the depth is within bounds and;
594+
# collapsing is disabled or the sub-entry's parent is 'current'.
595+
# The boolean is constant so is calculated outwith the loop.
596+
keep_bullet_list_sub_nodes = depth <= 1 or (
597+
(depth <= maxdepth or maxdepth <= 0)
598+
and (not collapse or is_current or 'iscurrent' in node)
599+
)
600+
if not keep_bullet_list_sub_nodes and not initial_call:
601+
node.children = []
602+
return
603+
depth += 1
604+
for subnode in node.children:
605+
_toctree_prune_seq(
606+
subnode, depth, maxdepth, collapse, tags, is_current='iscurrent' in node
607+
)
608+
return
609+
610+
if isinstance(node, addnodes.toctree):
611+
return
612+
613+
if isinstance(node, addnodes.only):
614+
# only keep children if the only node matches the tags
615+
if not _only_node_keep_children(node, tags):
616+
node.children = []
617+
return
618+
for subnode in node.children:
619+
_toctree_prune_seq(
620+
subnode, depth, maxdepth, collapse, tags, is_current='iscurrent' in node
621+
)
622+
return
623+
624+
if isinstance(node, (nodes.reference, nodes.title)):
625+
return
626+
627+
msg = f'Unexpected node type {node.__class__.__name__!r}!'
628+
raise ValueError(msg)
629+
630+
562631
def _get_toctree_ancestors(
563632
toctree_includes: dict[str, list[str]],
564633
docname: str,

0 commit comments

Comments
 (0)