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
.maxint
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 try: # let the writer (or output software) build the contents list?
90 toc_by_writer
= self
.document
.settings
.use_latex_toc
91 except AttributeError:
93 details
= self
.startnode
.details
94 if 'local' in details
:
95 startnode
= self
.startnode
.parent
.parent
96 while not (isinstance(startnode
, nodes
.section
)
97 or isinstance(startnode
, nodes
.document
)):
98 # find the ToC root: a direct ancestor of startnode
99 startnode
= startnode
.parent
101 startnode
= self
.document
102 self
.toc_id
= self
.startnode
.parent
['ids'][0]
103 if 'backlinks' in details
:
104 self
.backlinks
= details
['backlinks']
106 self
.backlinks
= self
.document
.settings
.toc_backlinks
108 # move customization settings to the parent node
109 self
.startnode
.parent
.attributes
.update(details
)
110 self
.startnode
.parent
.remove(self
.startnode
)
112 contents
= self
.build_contents(startnode
)
114 self
.startnode
.replace_self(contents
)
116 self
.startnode
.parent
.parent
.remove(self
.startnode
.parent
)
118 def build_contents(self
, node
, level
=0):
120 sections
= [sect
for sect
in node
if isinstance(sect
, nodes
.section
)]
123 depth
= self
.startnode
.details
.get('depth', sys
.maxint
)
124 for section
in sections
:
126 auto
= title
.get('auto') # May be set by SectNum.
127 entrytext
= self
.copy_and_filter(title
)
128 reference
= nodes
.reference('', '', refid
=section
['ids'][0],
130 ref_id
= self
.document
.set_id(reference
)
131 entry
= nodes
.paragraph('', '', reference
)
132 item
= nodes
.list_item('', entry
)
133 if ( self
.backlinks
in ('entry', 'top')
134 and title
.next_node(nodes
.reference
) is None):
135 if self
.backlinks
== 'entry':
136 title
['refid'] = ref_id
137 elif self
.backlinks
== 'top':
138 title
['refid'] = self
.toc_id
140 subsects
= self
.build_contents(section
, level
)
144 contents
= nodes
.bullet_list('', *entries
)
146 contents
['classes'].append('auto-toc')
151 def copy_and_filter(self
, node
):
152 """Return a copy of a title, with references, images, etc. removed."""
153 visitor
= ContentsFilter(self
.document
)
154 node
.walkabout(visitor
)
155 return visitor
.get_entry_text()
158 class ContentsFilter(nodes
.TreeCopyVisitor
):
160 def get_entry_text(self
):
161 return self
.get_tree_copy().children
163 def visit_citation_reference(self
, node
):
166 def visit_footnote_reference(self
, node
):
169 def visit_image(self
, node
):
170 if node
.hasattr('alt'):
171 self
.parent
.append(nodes
.Text(node
['alt']))
174 def ignore_node_but_process_children(self
, node
):
175 raise nodes
.SkipDeparture
177 visit_interpreted
= ignore_node_but_process_children
178 visit_problematic
= ignore_node_but_process_children
179 visit_reference
= ignore_node_but_process_children
180 visit_target
= ignore_node_but_process_children