__init__.py (4869B)
1 # -*- coding: utf-8 -*- 2 """ 3 sphinx_multitoc_numbering 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 A sphinx extension for continuous numbering of sections across toctrees. 6 """ 7 8 from typing import Dict, List, Set, Tuple 9 from typing import cast 10 11 from docutils import nodes 12 from docutils.nodes import Element 13 14 from sphinx import addnodes 15 from sphinx.environment import BuildEnvironment 16 from sphinx.locale import __ 17 from sphinx.util import url_re, logging 18 19 logger = logging.getLogger(__name__) 20 21 __version__ = "0.1.3" 22 """sphinx-multitoc-numbering version""" 23 24 25 def build_init_handler(app): 26 from sphinx.util.console import bold 27 28 logger.info(bold("sphinx-multitoc-numbering v%s:") + " Loaded", __version__) 29 30 31 def assign_section_numbers(self, env: BuildEnvironment) -> List[str]: 32 """Assign a section number to each heading under a numbered toctree.""" 33 # a list of all docnames whose section numbers changed 34 rewrite_needed = [] 35 36 assigned = set() # type: Set[str] 37 old_secnumbers = env.toc_secnumbers 38 env.toc_secnumbers = {} 39 self.last_chapter_number = 0 40 41 def _walk_toc( 42 node: Element, secnums: Dict, depth: int, titlenode: nodes.title = None 43 ) -> None: 44 # titlenode is the title of the document, it will get assigned a 45 # secnumber too, so that it shows up in next/prev/parent rellinks 46 for subnode in node.children: 47 if isinstance(subnode, nodes.bullet_list): 48 numstack.append(0) 49 _walk_toc(subnode, secnums, depth - 1, titlenode) 50 numstack.pop() 51 titlenode = None 52 elif isinstance(subnode, nodes.list_item): 53 _walk_toc(subnode, secnums, depth, titlenode) 54 titlenode = None 55 elif isinstance(subnode, addnodes.only): 56 # at this stage we don't know yet which sections are going 57 # to be included; just include all of them, even if it leads 58 # to gaps in the numbering 59 _walk_toc(subnode, secnums, depth, titlenode) 60 titlenode = None 61 elif isinstance(subnode, addnodes.compact_paragraph): 62 numstack[-1] += 1 63 reference = cast(nodes.reference, subnode[0]) 64 65 # if a new chapter is encountered increment the chapter number 66 if len(numstack) == 1: 67 self.last_chapter_number += 1 68 if depth > 0: 69 number = list(numstack) 70 secnums[reference["anchorname"]] = tuple(numstack) 71 else: 72 number = None 73 secnums[reference["anchorname"]] = None 74 reference["secnumber"] = number 75 if titlenode: 76 titlenode["secnumber"] = number 77 titlenode = None 78 elif isinstance(subnode, addnodes.toctree): 79 _walk_toctree(subnode, depth) 80 81 def _walk_toctree(toctreenode: addnodes.toctree, depth: int) -> None: 82 if depth == 0: 83 return 84 for (title, ref) in toctreenode["entries"]: 85 if url_re.match(ref) or ref == "self": 86 # don't mess with those 87 continue 88 elif ref in assigned: 89 logger.warning( 90 __( 91 "%s is already assigned section numbers " 92 "(nested numbered toctree?)" 93 ), 94 ref, 95 location=toctreenode, 96 type="toc", 97 subtype="secnum", 98 ) 99 elif ref in env.tocs: 100 secnums = {} # type: Dict[str, Tuple[int, ...]] 101 env.toc_secnumbers[ref] = secnums 102 assigned.add(ref) 103 _walk_toc(env.tocs[ref], secnums, depth, env.titles.get(ref)) 104 if secnums != old_secnumbers.get(ref): 105 rewrite_needed.append(ref) 106 107 # rearrange it to respect ordering in toctree directives 108 rearranged_numbered_toctrees = [] 109 for toc in env.tocs: 110 if toc in env.numbered_toctrees: 111 rearranged_numbered_toctrees.append(toc) 112 113 for docname in rearranged_numbered_toctrees: 114 assigned.add(docname) 115 doctree = env.get_doctree(docname) 116 for toctreenode in doctree.traverse(addnodes.toctree): 117 depth = toctreenode.get("numbered", 0) 118 if depth: 119 # every numbered toctree continues the numbering 120 numstack = [self.last_chapter_number] 121 _walk_toctree(toctreenode, depth) 122 123 return rewrite_needed 124 125 126 def setup(app): 127 from sphinx.environment.collectors.toctree import TocTreeCollector 128 129 app.connect("builder-inited", build_init_handler) 130 TocTreeCollector.assign_section_numbers = assign_section_numbers