summaryrefslogtreecommitdiff
path: root/docutils/transforms/parts.py
diff options
context:
space:
mode:
Diffstat (limited to 'docutils/transforms/parts.py')
-rw-r--r--docutils/transforms/parts.py171
1 files changed, 171 insertions, 0 deletions
diff --git a/docutils/transforms/parts.py b/docutils/transforms/parts.py
new file mode 100644
index 000000000..1e275c78a
--- /dev/null
+++ b/docutils/transforms/parts.py
@@ -0,0 +1,171 @@
+# Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov
+# Contact: goodger@users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Transforms related to document parts.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import re
+import sys
+from docutils import nodes, utils
+from docutils.transforms import TransformError, Transform
+
+
+class SectNum(Transform):
+
+ """
+ Automatically assigns numbers to the titles of document sections.
+
+ It is possible to limit the maximum section level for which the numbers
+ are added. For those sections that are auto-numbered, the "autonum"
+ attribute is set, informing the contents table generator that a different
+ form of the TOC should be used.
+ """
+
+ default_priority = 710
+ """Should be applied before `Contents`."""
+
+ def apply(self):
+ self.maxdepth = self.startnode.details.get('depth', sys.maxint)
+ self.startvalue = self.startnode.details.get('start', 1)
+ self.prefix = self.startnode.details.get('prefix', '')
+ self.suffix = self.startnode.details.get('suffix', '')
+ self.startnode.parent.remove(self.startnode)
+ if self.document.settings.sectnum_xform:
+ self.update_section_numbers(self.document)
+
+ def update_section_numbers(self, node, prefix=(), depth=0):
+ depth += 1
+ if prefix:
+ sectnum = 1
+ else:
+ sectnum = self.startvalue
+ for child in node:
+ if isinstance(child, nodes.section):
+ numbers = prefix + (str(sectnum),)
+ title = child[0]
+ # Use   for spacing:
+ generated = nodes.generated(
+ '', (self.prefix + '.'.join(numbers) + self.suffix
+ + u'\u00a0' * 3),
+ classes=['sectnum'])
+ title.insert(0, generated)
+ title['auto'] = 1
+ if depth < self.maxdepth:
+ self.update_section_numbers(child, numbers, depth)
+ sectnum += 1
+
+
+class Contents(Transform):
+
+ """
+ This transform generates a table of contents from the entire document tree
+ or from a single branch. It locates "section" elements and builds them
+ into a nested bullet list, which is placed within a "topic" created by the
+ contents directive. A title is either explicitly specified, taken from
+ the appropriate language module, or omitted (local table of contents).
+ The depth may be specified. Two-way references between the table of
+ contents and section titles are generated (requires Writer support).
+
+ This transform requires a startnode, which which contains generation
+ options and provides the location for the generated table of contents (the
+ startnode is replaced by the table of contents "topic").
+ """
+
+ default_priority = 720
+
+ def apply(self):
+ details = self.startnode.details
+ if details.has_key('local'):
+ startnode = self.startnode.parent.parent
+ while not (isinstance(startnode, nodes.section)
+ or isinstance(startnode, nodes.document)):
+ # find the ToC root: a direct ancestor of startnode
+ startnode = startnode.parent
+ else:
+ startnode = self.document
+ self.toc_id = self.startnode.parent['ids'][0]
+ if details.has_key('backlinks'):
+ self.backlinks = details['backlinks']
+ else:
+ self.backlinks = self.document.settings.toc_backlinks
+ contents = self.build_contents(startnode)
+ if len(contents):
+ self.startnode.replace_self(contents)
+ else:
+ self.startnode.parent.parent.remove(self.startnode.parent)
+
+ def build_contents(self, node, level=0):
+ level += 1
+ sections = []
+ i = len(node) - 1
+ while i >= 0 and isinstance(node[i], nodes.section):
+ sections.append(node[i])
+ i -= 1
+ sections.reverse()
+ entries = []
+ autonum = 0
+ depth = self.startnode.details.get('depth', sys.maxint)
+ for section in sections:
+ title = section[0]
+ auto = title.get('auto') # May be set by SectNum.
+ entrytext = self.copy_and_filter(title)
+ reference = nodes.reference('', '', refid=section['ids'][0],
+ *entrytext)
+ ref_id = self.document.set_id(reference)
+ entry = nodes.paragraph('', '', reference)
+ item = nodes.list_item('', entry)
+ if ( self.backlinks in ('entry', 'top')
+ and title.next_node(nodes.reference) is None):
+ if self.backlinks == 'entry':
+ title['refid'] = ref_id
+ elif self.backlinks == 'top':
+ title['refid'] = self.toc_id
+ if level < depth:
+ subsects = self.build_contents(section, level)
+ item += subsects
+ entries.append(item)
+ if entries:
+ contents = nodes.bullet_list('', *entries)
+ if auto:
+ contents['classes'].append('auto-toc')
+ return contents
+ else:
+ return []
+
+ def copy_and_filter(self, node):
+ """Return a copy of a title, with references, images, etc. removed."""
+ visitor = ContentsFilter(self.document)
+ node.walkabout(visitor)
+ return visitor.get_entry_text()
+
+
+class ContentsFilter(nodes.TreeCopyVisitor):
+
+ def get_entry_text(self):
+ return self.get_tree_copy().children
+
+ def visit_citation_reference(self, node):
+ raise nodes.SkipNode
+
+ def visit_footnote_reference(self, node):
+ raise nodes.SkipNode
+
+ def visit_image(self, node):
+ if node.hasattr('alt'):
+ self.parent.append(nodes.Text(node['alt']))
+ raise nodes.SkipNode
+
+ def ignore_node_but_process_children(self, node):
+ raise nodes.SkipDeparture
+
+ visit_interpreted = ignore_node_but_process_children
+ visit_problematic = ignore_node_but_process_children
+ visit_reference = ignore_node_but_process_children
+ visit_target = ignore_node_but_process_children