Skip to content

Commit 12ac5d4

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

File tree

1 file changed

+75
-1
lines changed

1 file changed

+75
-1
lines changed

sphinx/environment/adapters/toctree.py

Lines changed: 75 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
@@ -482,6 +482,7 @@ def _toctree_add_classes(node: Element, depth: int, docname: str) -> None:
482482
subnode = subnode.parent
483483

484484

485+
# Note: Equivalent to _toctree_prune, but prunes into a copy.
485486
def _toctree_copy[ET: Element](
486487
node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tags
487488
) -> ET:
@@ -493,6 +494,7 @@ def _toctree_copy[ET: Element](
493494
return copied[0] # type: ignore[return-value]
494495

495496

497+
# Note: Equivalent to _toctree_prune_seq, but prunes into a copy.
496498
def _toctree_copy_seq(
497499
node: Node,
498500
depth: int,
@@ -559,6 +561,78 @@ def _toctree_copy_seq(
559561
raise ValueError(msg)
560562

561563

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

0 commit comments

Comments
 (0)