@@ -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+
562631def _get_toctree_ancestors (
563632 toctree_includes : dict [str , list [str ]],
564633 docname : str ,
0 commit comments