taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

__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