1 | # Author: David Goodger |
---|
2 | # Contact: goodger@users.sourceforge.net |
---|
3 | # Revision: $Revision: 3909 $ |
---|
4 | # Date: $Date: 2005-09-26 20:17:31 +0200 (Mon, 26 Sep 2005) $ |
---|
5 | # Copyright: This module has been placed in the public domain. |
---|
6 | |
---|
7 | """ |
---|
8 | Miscellaneous transforms. |
---|
9 | """ |
---|
10 | |
---|
11 | __docformat__ = 'reStructuredText' |
---|
12 | |
---|
13 | from docutils import nodes |
---|
14 | from docutils.transforms import Transform, TransformError |
---|
15 | |
---|
16 | |
---|
17 | class CallBack(Transform): |
---|
18 | |
---|
19 | """ |
---|
20 | Inserts a callback into a document. The callback is called when the |
---|
21 | transform is applied, which is determined by its priority. |
---|
22 | |
---|
23 | For use with `nodes.pending` elements. Requires a ``details['callback']`` |
---|
24 | entry, a bound method or function which takes one parameter: the pending |
---|
25 | node. Other data can be stored in the ``details`` attribute or in the |
---|
26 | object hosting the callback method. |
---|
27 | """ |
---|
28 | |
---|
29 | default_priority = 990 |
---|
30 | |
---|
31 | def apply(self): |
---|
32 | pending = self.startnode |
---|
33 | pending.details['callback'](pending) |
---|
34 | pending.parent.remove(pending) |
---|
35 | |
---|
36 | |
---|
37 | class ClassAttribute(Transform): |
---|
38 | |
---|
39 | """ |
---|
40 | Move the "class" attribute specified in the "pending" node into the |
---|
41 | immediately following non-comment element. |
---|
42 | """ |
---|
43 | |
---|
44 | default_priority = 210 |
---|
45 | |
---|
46 | def apply(self): |
---|
47 | pending = self.startnode |
---|
48 | parent = pending.parent |
---|
49 | child = pending |
---|
50 | while parent: |
---|
51 | # Check for appropriate following siblings: |
---|
52 | for index in range(parent.index(child) + 1, len(parent)): |
---|
53 | element = parent[index] |
---|
54 | if (isinstance(element, nodes.Invisible) or |
---|
55 | isinstance(element, nodes.system_message)): |
---|
56 | continue |
---|
57 | element['classes'] += pending.details['class'] |
---|
58 | pending.parent.remove(pending) |
---|
59 | return |
---|
60 | else: |
---|
61 | # At end of section or container; apply to sibling |
---|
62 | child = parent |
---|
63 | parent = parent.parent |
---|
64 | error = self.document.reporter.error( |
---|
65 | 'No suitable element following "%s" directive' |
---|
66 | % pending.details['directive'], |
---|
67 | nodes.literal_block(pending.rawsource, pending.rawsource), |
---|
68 | line=pending.line) |
---|
69 | pending.replace_self(error) |
---|
70 | |
---|
71 | |
---|
72 | class Transitions(Transform): |
---|
73 | |
---|
74 | """ |
---|
75 | Move transitions at the end of sections up the tree. Complain |
---|
76 | on transitions after a title, at the beginning or end of the |
---|
77 | document, and after another transition. |
---|
78 | |
---|
79 | For example, transform this:: |
---|
80 | |
---|
81 | <section> |
---|
82 | ... |
---|
83 | <transition> |
---|
84 | <section> |
---|
85 | ... |
---|
86 | |
---|
87 | into this:: |
---|
88 | |
---|
89 | <section> |
---|
90 | ... |
---|
91 | <transition> |
---|
92 | <section> |
---|
93 | ... |
---|
94 | """ |
---|
95 | |
---|
96 | default_priority = 830 |
---|
97 | |
---|
98 | def apply(self): |
---|
99 | for node in self.document.traverse(nodes.transition): |
---|
100 | self.visit_transition(node) |
---|
101 | |
---|
102 | def visit_transition(self, node): |
---|
103 | index = node.parent.index(node) |
---|
104 | error = None |
---|
105 | if (index == 0 or |
---|
106 | isinstance(node.parent[0], nodes.title) and |
---|
107 | (index == 1 or |
---|
108 | isinstance(node.parent[1], nodes.subtitle) and |
---|
109 | index == 2)): |
---|
110 | assert (isinstance(node.parent, nodes.document) or |
---|
111 | isinstance(node.parent, nodes.section)) |
---|
112 | error = self.document.reporter.error( |
---|
113 | 'Document or section may not begin with a transition.', |
---|
114 | line=node.line) |
---|
115 | elif isinstance(node.parent[index - 1], nodes.transition): |
---|
116 | error = self.document.reporter.error( |
---|
117 | 'At least one body element must separate transitions; ' |
---|
118 | 'adjacent transitions are not allowed.', line=node.line) |
---|
119 | if error: |
---|
120 | # Insert before node and update index. |
---|
121 | node.parent.insert(index, error) |
---|
122 | index += 1 |
---|
123 | assert index < len(node.parent) |
---|
124 | if index != len(node.parent) - 1: |
---|
125 | # No need to move the node. |
---|
126 | return |
---|
127 | # Node behind which the transition is to be moved. |
---|
128 | sibling = node |
---|
129 | # While sibling is the last node of its parent. |
---|
130 | while index == len(sibling.parent) - 1: |
---|
131 | sibling = sibling.parent |
---|
132 | # If sibling is the whole document (i.e. it has no parent). |
---|
133 | if sibling.parent is None: |
---|
134 | # Transition at the end of document. Do not move the |
---|
135 | # transition up, and place an error behind. |
---|
136 | error = self.document.reporter.error( |
---|
137 | 'Document may not end with a transition.', |
---|
138 | line=node.line) |
---|
139 | node.parent.insert(node.parent.index(node) + 1, error) |
---|
140 | return |
---|
141 | index = sibling.parent.index(sibling) |
---|
142 | # Remove the original transition node. |
---|
143 | node.parent.remove(node) |
---|
144 | # Insert the transition after the sibling. |
---|
145 | sibling.parent.insert(index + 1, node) |
---|