| # $Id: misc.py 6314 2010-04-26 10:04:17Z milde $ |
| # Author: David Goodger <[email protected]> |
| # Copyright: This module has been placed in the public domain. |
| |
| """ |
| Miscellaneous transforms. |
| """ |
| |
| __docformat__ = 'reStructuredText' |
| |
| from docutils import nodes |
| from docutils.transforms import Transform, TransformError |
| |
| |
| class CallBack(Transform): |
| |
| """ |
| Inserts a callback into a document. The callback is called when the |
| transform is applied, which is determined by its priority. |
| |
| For use with `nodes.pending` elements. Requires a ``details['callback']`` |
| entry, a bound method or function which takes one parameter: the pending |
| node. Other data can be stored in the ``details`` attribute or in the |
| object hosting the callback method. |
| """ |
| |
| default_priority = 990 |
| |
| def apply(self): |
| pending = self.startnode |
| pending.details['callback'](pending) |
| pending.parent.remove(pending) |
| |
| |
| class ClassAttribute(Transform): |
| |
| """ |
| Move the "class" attribute specified in the "pending" node into the |
| immediately following non-comment element. |
| """ |
| |
| default_priority = 210 |
| |
| def apply(self): |
| pending = self.startnode |
| parent = pending.parent |
| child = pending |
| while parent: |
| # Check for appropriate following siblings: |
| for index in range(parent.index(child) + 1, len(parent)): |
| element = parent[index] |
| if (isinstance(element, nodes.Invisible) or |
| isinstance(element, nodes.system_message)): |
| continue |
| element['classes'] += pending.details['class'] |
| pending.parent.remove(pending) |
| return |
| else: |
| # At end of section or container; apply to sibling |
| child = parent |
| parent = parent.parent |
| error = self.document.reporter.error( |
| 'No suitable element following "%s" directive' |
| % pending.details['directive'], |
| nodes.literal_block(pending.rawsource, pending.rawsource), |
| line=pending.line) |
| pending.replace_self(error) |
| |
| |
| class Transitions(Transform): |
| |
| """ |
| Move transitions at the end of sections up the tree. Complain |
| on transitions after a title, at the beginning or end of the |
| document, and after another transition. |
| |
| For example, transform this:: |
| |
| <section> |
| ... |
| <transition> |
| <section> |
| ... |
| |
| into this:: |
| |
| <section> |
| ... |
| <transition> |
| <section> |
| ... |
| """ |
| |
| default_priority = 830 |
| |
| def apply(self): |
| for node in self.document.traverse(nodes.transition): |
| self.visit_transition(node) |
| |
| def visit_transition(self, node): |
| index = node.parent.index(node) |
| error = None |
| if (index == 0 or |
| isinstance(node.parent[0], nodes.title) and |
| (index == 1 or |
| isinstance(node.parent[1], nodes.subtitle) and |
| index == 2)): |
| assert (isinstance(node.parent, nodes.document) or |
| isinstance(node.parent, nodes.section)) |
| error = self.document.reporter.error( |
| 'Document or section may not begin with a transition.', |
| source=node.source, line=node.line) |
| elif isinstance(node.parent[index - 1], nodes.transition): |
| error = self.document.reporter.error( |
| 'At least one body element must separate transitions; ' |
| 'adjacent transitions are not allowed.', |
| source=node.source, line=node.line) |
| if error: |
| # Insert before node and update index. |
| node.parent.insert(index, error) |
| index += 1 |
| assert index < len(node.parent) |
| if index != len(node.parent) - 1: |
| # No need to move the node. |
| return |
| # Node behind which the transition is to be moved. |
| sibling = node |
| # While sibling is the last node of its parent. |
| while index == len(sibling.parent) - 1: |
| sibling = sibling.parent |
| # If sibling is the whole document (i.e. it has no parent). |
| if sibling.parent is None: |
| # Transition at the end of document. Do not move the |
| # transition up, and place an error behind. |
| error = self.document.reporter.error( |
| 'Document may not end with a transition.', |
| line=node.line) |
| node.parent.insert(node.parent.index(node) + 1, error) |
| return |
| index = sibling.parent.index(sibling) |
| # Remove the original transition node. |
| node.parent.remove(node) |
| # Insert the transition after the sibling. |
| sibling.parent.insert(index + 1, node) |