1
# -*- coding: utf-8 -*-
2 1
"""
3
    sphinx_multitoc_numbering
4
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
    A sphinx extension for continuous numbering of sections across toctrees.
6
"""
7

8 1
from typing import Dict, List, Set, Tuple
9 1
from typing import cast
10

11 1
from docutils import nodes
12 1
from docutils.nodes import Element
13

14 1
from sphinx import addnodes
15 1
from sphinx.environment import BuildEnvironment
16 1
from sphinx.locale import __
17 1
from sphinx.util import url_re, logging
18

19 1
logger = logging.getLogger(__name__)
20

21 1
__version__ = "0.1.2"
22
"""sphinx-multitoc-numbering version"""
23

24

25 1
def assign_section_numbers(self, env: BuildEnvironment) -> List[str]:
26
    """Assign a section number to each heading under a numbered toctree."""
27
    # a list of all docnames whose section numbers changed
28 1
    rewrite_needed = []
29

30 1
    assigned = set()  # type: Set[str]
31 1
    old_secnumbers = env.toc_secnumbers
32 1
    env.toc_secnumbers = {}
33 1
    self.last_chapter_number = 0
34

35 1
    def _walk_toc(
36
        node: Element, secnums: Dict, depth: int, titlenode: nodes.title = None
37
    ) -> None:
38
        # titlenode is the title of the document, it will get assigned a
39
        # secnumber too, so that it shows up in next/prev/parent rellinks
40 1
        for subnode in node.children:
41 1
            if isinstance(subnode, nodes.bullet_list):
42 1
                numstack.append(0)
43 1
                _walk_toc(subnode, secnums, depth - 1, titlenode)
44 1
                numstack.pop()
45 1
                titlenode = None
46 1
            elif isinstance(subnode, nodes.list_item):
47 1
                _walk_toc(subnode, secnums, depth, titlenode)
48 1
                titlenode = None
49 1
            elif isinstance(subnode, addnodes.only):
50
                # at this stage we don't know yet which sections are going
51
                # to be included; just include all of them, even if it leads
52
                # to gaps in the numbering
53 0
                _walk_toc(subnode, secnums, depth, titlenode)
54 0
                titlenode = None
55 1
            elif isinstance(subnode, addnodes.compact_paragraph):
56 1
                numstack[-1] += 1
57 1
                reference = cast(nodes.reference, subnode[0])
58

59
                # if a new chapter is encountered increment the chapter number
60 1
                if len(numstack) == 1:
61 1
                    self.last_chapter_number += 1
62 1
                if depth > 0:
63 1
                    number = list(numstack)
64 1
                    secnums[reference["anchorname"]] = tuple(numstack)
65
                else:
66 0
                    number = None
67 0
                    secnums[reference["anchorname"]] = None
68 1
                reference["secnumber"] = number
69 1
                if titlenode:
70 1
                    titlenode["secnumber"] = number
71 1
                    titlenode = None
72 1
            elif isinstance(subnode, addnodes.toctree):
73 1
                _walk_toctree(subnode, depth)
74

75 1
    def _walk_toctree(toctreenode: addnodes.toctree, depth: int) -> None:
76 1
        if depth == 0:
77 0
            return
78 1
        for (title, ref) in toctreenode["entries"]:
79 1
            if url_re.match(ref) or ref == "self":
80
                # don't mess with those
81 0
                continue
82 1
            elif ref in assigned:
83 0
                logger.warning(
84
                    __(
85
                        "%s is already assigned section numbers "
86
                        "(nested numbered toctree?)"
87
                    ),
88
                    ref,
89
                    location=toctreenode,
90
                    type="toc",
91
                    subtype="secnum",
92
                )
93 1
            elif ref in env.tocs:
94 1
                secnums = {}  # type: Dict[str, Tuple[int, ...]]
95 1
                env.toc_secnumbers[ref] = secnums
96 1
                assigned.add(ref)
97 1
                _walk_toc(env.tocs[ref], secnums, depth, env.titles.get(ref))
98 1
                if secnums != old_secnumbers.get(ref):
99 1
                    rewrite_needed.append(ref)
100

101
    # rearrange it to respect ordering in toctree directives
102 1
    rearranged_numbered_toctrees = []
103 1
    for toc in env.tocs:
104 1
        if toc in env.numbered_toctrees:
105 1
            rearranged_numbered_toctrees.append(toc)
106

107 1
    for docname in rearranged_numbered_toctrees:
108 1
        assigned.add(docname)
109 1
        doctree = env.get_doctree(docname)
110 1
        for toctreenode in doctree.traverse(addnodes.toctree):
111 1
            depth = toctreenode.get("numbered", 0)
112 1
            if depth:
113
                # every numbered toctree continues the numbering
114 1
                numstack = [self.last_chapter_number]
115 1
                _walk_toctree(toctreenode, depth)
116

117 1
    return rewrite_needed
118

119

120 1
def setup(app):
121 1
    from sphinx.environment.collectors.toctree import TocTreeCollector
122

123 1
    TocTreeCollector.assign_section_numbers = assign_section_numbers

Read our documentation on viewing source code .

Loading