1 #!/usr/bin/env python 2 3 """ 4 Moin wiki format document tree nodes. 5 6 Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 class Container: 23 24 "A container of document nodes." 25 26 def __init__(self, nodes): 27 self.nodes = nodes 28 29 def append(self, node): 30 self.nodes.append(node) 31 32 add = append 33 34 append_text = append 35 36 def empty(self): 37 return not self.nodes 38 39 def node(self, index): 40 try: 41 return self.nodes[index] 42 except IndexError: 43 return None 44 45 def normalise(self): 46 47 "Combine adjacent text nodes." 48 49 nodes = self.nodes 50 self.nodes = [] 51 text = None 52 53 for node in nodes: 54 55 # Open a text node or merge text into an open node. 56 57 if isinstance(node, Text): 58 if not text: 59 text = node 60 else: 61 text.merge(node) 62 63 # Close any open text node and append the current node. 64 65 else: 66 if text: 67 self.append(text) 68 text = None 69 self.append(node) 70 71 # Add any open text node. 72 73 if text: 74 self.append(text) 75 76 def __str__(self): 77 return self.prettyprint() 78 79 def _prettyprint(self, l, indent=""): 80 for node in self.nodes: 81 l.append(node.prettyprint(indent + " ")) 82 return "\n".join(l) 83 84 def _to_string(self, out): 85 for node in self.nodes: 86 node.to_string(out) 87 88 class Region(Container): 89 90 "A region of the page." 91 92 transparent_region_types = ["wiki"] 93 94 def __init__(self, nodes, level=0, indent=0, type=None): 95 Container.__init__(self, nodes) 96 self.level = level 97 self.indent = indent 98 self.type = type 99 100 def add(self, node): 101 last = self.node(-1) 102 if last and last.empty(): 103 self.nodes[-1] = node 104 else: 105 self.nodes.append(node) 106 107 def append_text(self, s): 108 if self.is_transparent(): 109 self.nodes[-1].append(s) 110 else: 111 self.append(s) 112 113 def have_end(self, s): 114 return self.level and s.startswith("}") and self.level == len(s) 115 116 def is_transparent(self): 117 return not self.level or self.type in self.transparent_region_types 118 119 def __repr__(self): 120 return "Region(%r, %r, %r, %r)" % (self.nodes, self.level, self.indent, self.type) 121 122 def prettyprint(self, indent=""): 123 l = ["%sRegion: level=%d indent=%d type=%s" % (indent, self.level, self.indent, self.type)] 124 return self._prettyprint(l, indent) 125 126 def to_string(self, out): 127 out.start_region(self.level, self.indent, self.type) 128 self._to_string(out) 129 out.end_region(self.level, self.indent, self.type) 130 131 class Block(Container): 132 133 "A block in the page." 134 135 def __repr__(self): 136 return "Block(%r)" % self.nodes 137 138 def prettyprint(self, indent=""): 139 l = ["%sBlock" % indent] 140 return self._prettyprint(l, indent) 141 142 def to_string(self, out): 143 out.start_block() 144 self._to_string(out) 145 out.end_block() 146 147 class DefItem(Container): 148 149 "A definition item." 150 151 def __init__(self, nodes, pad, extra): 152 Container.__init__(self, nodes) 153 self.pad = pad 154 self.extra = extra 155 156 def __repr__(self): 157 return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra) 158 159 def prettyprint(self, indent=""): 160 l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)] 161 return self._prettyprint(l, indent) 162 163 def to_string(self, out): 164 out.start_defitem(self.pad, self.extra) 165 self._to_string(out) 166 out.end_defitem(self.pad, self.extra) 167 168 class DefTerm(Container): 169 170 "A definition term." 171 172 def __init__(self, nodes, pad): 173 Container.__init__(self, nodes) 174 self.pad = pad 175 176 def __repr__(self): 177 return "DefTerm(%r, %r)" % (self.nodes, self.pad) 178 179 def prettyprint(self, indent=""): 180 l = ["%sDefTerm: pad=%r" % (indent, self.pad)] 181 return self._prettyprint(l, indent) 182 183 def to_string(self, out): 184 out.start_defterm(self.pad) 185 self._to_string(out) 186 out.end_defterm(self.pad) 187 188 class Heading(Container): 189 190 "A heading." 191 192 def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""): 193 Container.__init__(self, nodes) 194 self.level = level 195 self.start_extra = start_extra 196 self.start_pad = start_pad 197 self.end_pad = end_pad 198 self.end_extra = end_extra 199 200 def __repr__(self): 201 return "Heading(%r, %d, %r, %r, %r, %r)" % ( 202 self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra) 203 204 def prettyprint(self, indent=""): 205 l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % ( 206 indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)] 207 return self._prettyprint(l, indent) 208 209 def to_string(self, out): 210 out.start_heading(self.level, self.start_extra, self.start_pad) 211 self._to_string(out) 212 out.end_heading(self.level, self.end_pad, self.end_extra) 213 214 class ListItem(Container): 215 216 "A list item." 217 218 def __init__(self, nodes, indent, marker, space): 219 Container.__init__(self, nodes) 220 self.indent = indent 221 self.marker = marker 222 self.space = space 223 224 def __repr__(self): 225 return "ListItem(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space) 226 227 def prettyprint(self, indent=""): 228 l = ["%sListItem: indent=%d marker=%r space=%r" % (indent, self.indent, self.marker, self.space)] 229 return self._prettyprint(l, indent) 230 231 def to_string(self, out): 232 out.start_listitem(self.indent, self.marker, self.space) 233 self._to_string(out) 234 out.end_listitem(self.indent, self.marker) 235 236 237 238 class Node: 239 240 "A document node without children." 241 242 def empty(self): 243 return False 244 245 class Break(Node): 246 247 "A paragraph break." 248 249 def __repr__(self): 250 return "Break()" 251 252 def prettyprint(self, indent=""): 253 return "%sBreak" % indent 254 255 def to_string(self, out): 256 out.break_() 257 258 class Rule(Node): 259 260 "A horizontal rule." 261 262 def __init__(self, length): 263 self.length = length 264 265 def __repr__(self): 266 return "Rule(%d)" % self.length 267 268 def prettyprint(self, indent=""): 269 return "%sRule: %d" % (indent, self.length) 270 271 def to_string(self, out): 272 out.rule(self.length) 273 274 class Text(Node): 275 276 "A text node." 277 278 def __init__(self, s): 279 self.s = s 280 281 def empty(self): 282 return not self.s 283 284 def merge(self, text): 285 self.s += text.s 286 287 def __repr__(self): 288 return "Text(%r)" % self.s 289 290 def prettyprint(self, indent=""): 291 return "%sText: %r" % (indent, self.s) 292 293 def to_string(self, out): 294 out.text(self.s) 295 296 # vim: tabstop=4 expandtab shiftwidth=4