2 # Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer; Dmitry Jemerov
3 # Copyright: This module has been placed in the public domain.
6 Transforms related to document parts.
9 __docformat__
= 'reStructuredText'
14 from docutils
import nodes
, utils
15 from docutils
.transforms
import TransformError
, Transform
18 class SectNum(Transform
):
21 Automatically assigns numbers to the titles of document sections.
23 It is possible to limit the maximum section level for which the numbers
24 are added. For those sections that are auto-numbered, the "autonum"
25 attribute is set, informing the contents table generator that a different
26 form of the TOC should be used.
29 default_priority
= 710
30 """Should be applied before `Contents`."""
33 self
.maxdepth
= self
.startnode
.details
.get('depth', None)
34 self
.startvalue
= self
.startnode
.details
.get('start', 1)
35 self
.prefix
= self
.startnode
.details
.get('prefix', '')
36 self
.suffix
= self
.startnode
.details
.get('suffix', '')
37 self
.startnode
.parent
.remove(self
.startnode
)
38 if self
.document
.settings
.sectnum_xform
:
39 if self
.maxdepth
is None:
40 self
.maxdepth
= sys
.maxsize
41 self
.update_section_numbers(self
.document
)
42 else: # store details for eventual section numbering by the writer
43 self
.document
.settings
.sectnum_depth
= self
.maxdepth
44 self
.document
.settings
.sectnum_start
= self
.startvalue
45 self
.document
.settings
.sectnum_prefix
= self
.prefix
46 self
.document
.settings
.sectnum_suffix
= self
.suffix
48 def update_section_numbers(self
, node
, prefix
=(), depth
=0):
53 sectnum
= self
.startvalue
55 if isinstance(child
, nodes
.section
):
56 numbers
= prefix
+ (str(sectnum
),)
58 # Use for spacing:
59 generated
= nodes
.generated(
60 '', (self
.prefix
+ '.'.join(numbers
) + self
.suffix
63 title
.insert(0, generated
)
65 if depth
< self
.maxdepth
:
66 self
.update_section_numbers(child
, numbers
, depth
)
70 class Contents(Transform
):
73 This transform generates a table of contents from the entire document tree
74 or from a single branch. It locates "section" elements and builds them
75 into a nested bullet list, which is placed within a "topic" created by the
76 contents directive. A title is either explicitly specified, taken from
77 the appropriate language module, or omitted (local table of contents).
78 The depth may be specified. Two-way references between the table of
79 contents and section titles are generated (requires Writer support).
81 This transform requires a startnode, which contains generation
82 options and provides the location for the generated table of contents (the
83 startnode is replaced by the table of contents "topic").
86 default_priority
= 720
89 # let the writer (or output software) build the contents list?
90 toc_by_writer
= getattr(self
.document
.settings
, 'use_latex_toc', False)
91 details
= self
.startnode
.details
92 if 'local' in details
:
93 startnode
= self
.startnode
.parent
.parent
94 while not (isinstance(startnode
, nodes
.section
)
95 or isinstance(startnode
, nodes
.document
)):
96 # find the ToC root: a direct ancestor of startnode
97 startnode
= startnode
.parent
99 startnode
= self
.document
100 self
.toc_id
= self
.startnode
.parent
['ids'][0]
101 if 'backlinks' in details
:
102 self
.backlinks
= details
['backlinks']
104 self
.backlinks
= self
.document
.settings
.toc_backlinks
106 # move customization settings to the parent node
107 self
.startnode
.parent
.attributes
.update(details
)
108 self
.startnode
.parent
.remove(self
.startnode
)
110 contents
= self
.build_contents(startnode
)
112 self
.startnode
.replace_self(contents
)
114 self
.startnode
.parent
.parent
.remove(self
.startnode
.parent
)
116 def build_contents(self
, node
, level
=0):
118 sections
= [sect
for sect
in node
if isinstance(sect
, nodes
.section
)]
121 depth
= self
.startnode
.details
.get('depth', sys
.maxsize
)
122 for section
in sections
:
124 auto
= title
.get('auto') # May be set by SectNum.
125 entrytext
= self
.copy_and_filter(title
)
126 reference
= nodes
.reference('', '', refid
=section
['ids'][0],
128 ref_id
= self
.document
.set_id(reference
,
129 suggested_prefix
='toc-entry')
130 entry
= nodes
.paragraph('', '', reference
)
131 item
= nodes
.list_item('', entry
)
132 if ( self
.backlinks
in ('entry', 'top')
133 and title
.next_node(nodes
.reference
) is None):
134 if self
.backlinks
== 'entry':
135 title
['refid'] = ref_id
136 elif self
.backlinks
== 'top':
137 title
['refid'] = self
.toc_id
139 subsects
= self
.build_contents(section
, level
)
143 contents
= nodes
.bullet_list('', *entries
)
145 contents
['classes'].append('auto-toc')
150 def copy_and_filter(self
, node
):
151 """Return a copy of a title, with references, images, etc. removed."""
152 visitor
= ContentsFilter(self
.document
)
153 node
.walkabout(visitor
)
154 return visitor
.get_entry_text()
157 class ContentsFilter(nodes
.TreeCopyVisitor
):
159 def get_entry_text(self
):
160 return self
.get_tree_copy().children
162 def visit_citation_reference(self
, node
):
165 def visit_footnote_reference(self
, node
):
168 def visit_image(self
, node
):
169 if node
.hasattr('alt'):
170 self
.parent
.append(nodes
.Text(node
['alt']))
173 def ignore_node_but_process_children(self
, node
):
174 raise nodes
.SkipDeparture
176 visit_problematic
= ignore_node_but_process_children
177 visit_reference
= ignore_node_but_process_children
178 visit_target
= ignore_node_but_process_children