1.1 --- a/moinformat/tree.py Tue Jul 24 10:47:53 2018 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,621 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -Moin wiki format document tree nodes.
1.8 -
1.9 -Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.10 -
1.11 -This program is free software; you can redistribute it and/or modify it under
1.12 -the terms of the GNU General Public License as published by the Free Software
1.13 -Foundation; either version 3 of the License, or (at your option) any later
1.14 -version.
1.15 -
1.16 -This program is distributed in the hope that it will be useful, but WITHOUT
1.17 -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 -details.
1.20 -
1.21 -You should have received a copy of the GNU General Public License along with
1.22 -this program. If not, see <http://www.gnu.org/licenses/>.
1.23 -"""
1.24 -
1.25 -class Container:
1.26 -
1.27 - "A container of document nodes."
1.28 -
1.29 - def __init__(self, nodes):
1.30 - self.nodes = nodes
1.31 -
1.32 - # In principle, allow blocks within containers. Some nodes may forbid
1.33 - # them to simplify the document structure.
1.34 -
1.35 - self.allow_blocks = True
1.36 -
1.37 - def append(self, node):
1.38 - self.nodes.append(node)
1.39 -
1.40 - def append_many(self, nodes):
1.41 - for node in nodes:
1.42 - self.append(node)
1.43 -
1.44 - add = append
1.45 -
1.46 - append_inline = append
1.47 -
1.48 - def append_inline_many(self, nodes):
1.49 - for node in nodes:
1.50 - self.append_inline(node)
1.51 -
1.52 - def empty(self):
1.53 - return not self.nodes
1.54 -
1.55 - def node(self, index):
1.56 - try:
1.57 - return self.nodes[index]
1.58 - except IndexError:
1.59 - return None
1.60 -
1.61 - def normalise(self):
1.62 -
1.63 - "Combine adjacent text nodes."
1.64 -
1.65 - nodes = self.nodes
1.66 - self.nodes = []
1.67 - text = None
1.68 -
1.69 - for node in nodes:
1.70 -
1.71 - # Open a text node or merge text into an open node.
1.72 -
1.73 - if isinstance(node, Text):
1.74 - if not text:
1.75 - text = node
1.76 - else:
1.77 - text.merge(node)
1.78 -
1.79 - # Close any open text node and append the current node.
1.80 -
1.81 - else:
1.82 - if text:
1.83 - self.append(text)
1.84 - text = None
1.85 - self.append(node)
1.86 -
1.87 - # Add any open text node.
1.88 -
1.89 - if text:
1.90 - self.append(text)
1.91 -
1.92 - def __str__(self):
1.93 - return self.prettyprint()
1.94 -
1.95 - def _prettyprint(self, l, indent=""):
1.96 - for node in self.nodes:
1.97 - l.append(node.prettyprint(indent + " "))
1.98 - return "\n".join(l)
1.99 -
1.100 - def _to_string(self, out):
1.101 - for node in self.nodes:
1.102 - node.to_string(out)
1.103 -
1.104 -class Region(Container):
1.105 -
1.106 - "A region of the page."
1.107 -
1.108 - def __init__(self, nodes, level=0, indent=0, type=None, transparent=True, extra=None):
1.109 - Container.__init__(self, nodes)
1.110 - self.level = level
1.111 - self.indent = indent
1.112 - self.type = type
1.113 - self.transparent = transparent
1.114 - self.extra = extra
1.115 -
1.116 - def add(self, node):
1.117 - last = self.node(-1)
1.118 - if last and last.empty():
1.119 - self.nodes[-1] = node
1.120 - else:
1.121 - self.append(node)
1.122 -
1.123 - def append_inline(self, node):
1.124 - if self.transparent:
1.125 - self.nodes[-1].append(node)
1.126 - else:
1.127 - self.append(node)
1.128 -
1.129 - def have_end(self, s):
1.130 - return self.level and s.startswith("}") and self.level == len(s)
1.131 -
1.132 - def __repr__(self):
1.133 - return "Region(%r, %r, %r, %r, %r, %r)" % (self.nodes, self.level,
1.134 - self.indent, self.type, self.transparent, self.extra)
1.135 -
1.136 - def prettyprint(self, indent=""):
1.137 - l = ["%sRegion: level=%d indent=%d type=%s extra=%r" % (indent,
1.138 - self.level, self.indent, self.type, self.extra)]
1.139 - return self._prettyprint(l, indent)
1.140 -
1.141 - def to_string(self, out):
1.142 - out.start_region(self.level, self.indent, self.type, self.extra)
1.143 -
1.144 - # Obtain a serialiser for the region, if appropriate.
1.145 -
1.146 - serialiser = out.formats and out.formats.get(self.type)
1.147 - region_out = serialiser and serialiser(out.out, out.formats) or out
1.148 -
1.149 - # Serialise the region.
1.150 -
1.151 - self._to_string(region_out)
1.152 -
1.153 - out.end_region(self.level, self.indent, self.type, self.extra)
1.154 -
1.155 -
1.156 -
1.157 -# Block nodes.
1.158 -
1.159 -class Block(Container):
1.160 -
1.161 - "A block in the page."
1.162 -
1.163 - def __repr__(self):
1.164 - return "Block(%r)" % self.nodes
1.165 -
1.166 - def prettyprint(self, indent=""):
1.167 - l = ["%sBlock" % indent]
1.168 - return self._prettyprint(l, indent)
1.169 -
1.170 - def to_string(self, out):
1.171 - out.start_block()
1.172 - self._to_string(out)
1.173 - out.end_block()
1.174 -
1.175 -class DefItem(Container):
1.176 -
1.177 - "A definition item."
1.178 -
1.179 - def __init__(self, nodes, pad, extra):
1.180 - Container.__init__(self, nodes)
1.181 - self.pad = pad
1.182 - self.extra = extra
1.183 -
1.184 - def __repr__(self):
1.185 - return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra)
1.186 -
1.187 - def prettyprint(self, indent=""):
1.188 - l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)]
1.189 - return self._prettyprint(l, indent)
1.190 -
1.191 - def to_string(self, out):
1.192 - out.start_defitem(self.pad, self.extra)
1.193 - self._to_string(out)
1.194 - out.end_defitem(self.pad, self.extra)
1.195 -
1.196 -class DefTerm(Container):
1.197 -
1.198 - "A definition term."
1.199 -
1.200 - def __init__(self, nodes, pad):
1.201 - Container.__init__(self, nodes)
1.202 - self.pad = pad
1.203 -
1.204 - def __repr__(self):
1.205 - return "DefTerm(%r, %r)" % (self.nodes, self.pad)
1.206 -
1.207 - def prettyprint(self, indent=""):
1.208 - l = ["%sDefTerm: pad=%r" % (indent, self.pad)]
1.209 - return self._prettyprint(l, indent)
1.210 -
1.211 - def to_string(self, out):
1.212 - out.start_defterm(self.pad)
1.213 - self._to_string(out)
1.214 - out.end_defterm(self.pad)
1.215 -
1.216 -class FontStyle(Container):
1.217 -
1.218 - "Emphasised and/or strong text."
1.219 -
1.220 - def __init__(self, nodes, emphasis=False, strong=False):
1.221 - Container.__init__(self, nodes)
1.222 - self.emphasis = emphasis
1.223 - self.strong = strong
1.224 -
1.225 - def close_emphasis(self):
1.226 - if self.strong:
1.227 - span = FontStyle(self.nodes, emphasis=True)
1.228 - self.nodes = [span]
1.229 - self.emphasis = False
1.230 - return self.strong
1.231 -
1.232 - def close_strong(self):
1.233 - if self.emphasis:
1.234 - span = FontStyle(self.nodes, strong=True)
1.235 - self.nodes = [span]
1.236 - self.strong = False
1.237 - return self.emphasis
1.238 -
1.239 - def __repr__(self):
1.240 - return "FontStyle(%r, %r, %r)" % (self.nodes, self.emphasis, self.strong)
1.241 -
1.242 - def prettyprint(self, indent=""):
1.243 - l = ["%sFontStyle: emphasis=%r strong=%r" % (indent, self.emphasis, self.strong)]
1.244 - return self._prettyprint(l, indent)
1.245 -
1.246 - def to_string(self, out):
1.247 - if self.emphasis:
1.248 - out.start_emphasis()
1.249 - elif self.strong:
1.250 - out.start_strong()
1.251 - self._to_string(out)
1.252 - if self.emphasis:
1.253 - out.end_emphasis()
1.254 - elif self.strong:
1.255 - out.end_strong()
1.256 -
1.257 -class Heading(Container):
1.258 -
1.259 - "A heading."
1.260 -
1.261 - def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""):
1.262 - Container.__init__(self, nodes)
1.263 - self.level = level
1.264 - self.start_extra = start_extra
1.265 - self.start_pad = start_pad
1.266 - self.end_pad = end_pad
1.267 - self.end_extra = end_extra
1.268 -
1.269 - def __repr__(self):
1.270 - return "Heading(%r, %d, %r, %r, %r, %r)" % (
1.271 - self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)
1.272 -
1.273 - def prettyprint(self, indent=""):
1.274 - l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % (
1.275 - indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)]
1.276 - return self._prettyprint(l, indent)
1.277 -
1.278 - def to_string(self, out):
1.279 - out.start_heading(self.level, self.start_extra, self.start_pad)
1.280 - self._to_string(out)
1.281 - out.end_heading(self.level, self.end_pad, self.end_extra)
1.282 -
1.283 -class List(Container):
1.284 -
1.285 - "A list."
1.286 -
1.287 - def __init__(self, nodes, indent, marker, num):
1.288 - Container.__init__(self, nodes)
1.289 - self.indent = indent
1.290 - self.marker = marker
1.291 - self.num = num
1.292 -
1.293 - def __repr__(self):
1.294 - return "List(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.num)
1.295 -
1.296 - def prettyprint(self, indent=""):
1.297 - l = ["%sList: indent=%d marker=%r num=%r" % (indent, self.indent, self.marker, self.num)]
1.298 - return self._prettyprint(l, indent)
1.299 -
1.300 - def to_string(self, out):
1.301 - out.start_list(self.indent, self.marker, self.num)
1.302 - self._to_string(out)
1.303 - out.end_list(self.indent, self.marker, self.num)
1.304 -
1.305 -class ListItem(Container):
1.306 -
1.307 - "A list item."
1.308 -
1.309 - def __init__(self, nodes, indent, marker, space, num):
1.310 - Container.__init__(self, nodes)
1.311 - self.indent = indent
1.312 - self.marker = marker
1.313 - self.space = space
1.314 - self.num = num
1.315 -
1.316 - # Forbid blocks within list items for simpler structure.
1.317 -
1.318 - self.allow_blocks = False
1.319 -
1.320 - def __repr__(self):
1.321 - return "ListItem(%r, %r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space, self.num)
1.322 -
1.323 - def prettyprint(self, indent=""):
1.324 - l = ["%sListItem: indent=%d marker=%r space=%r num=%r" % (indent, self.indent, self.marker, self.space, self.num)]
1.325 - return self._prettyprint(l, indent)
1.326 -
1.327 - def to_string(self, out):
1.328 - out.start_listitem(self.indent, self.marker, self.space, self.num)
1.329 - self._to_string(out)
1.330 - out.end_listitem(self.indent, self.marker, self.space, self.num)
1.331 -
1.332 -class TableAttrs(Container):
1.333 -
1.334 - "A collection of table attributes."
1.335 -
1.336 - def __repr__(self):
1.337 - return "TableAttrs(%r)" % self.nodes
1.338 -
1.339 - def prettyprint(self, indent=""):
1.340 - l = ["%sTableAttrs:" % indent]
1.341 - return self._prettyprint(l, indent)
1.342 -
1.343 - def to_string(self, out):
1.344 - out.start_table_attrs()
1.345 - self._to_string(out)
1.346 - out.end_table_attrs()
1.347 -
1.348 -class Table(Container):
1.349 -
1.350 - "A table."
1.351 -
1.352 - def __repr__(self):
1.353 - return "Table(%r)" % self.nodes
1.354 -
1.355 - def prettyprint(self, indent=""):
1.356 - l = ["%sTable:" % indent]
1.357 - return self._prettyprint(l, indent)
1.358 -
1.359 - def to_string(self, out):
1.360 - out.start_table()
1.361 - self._to_string(out)
1.362 - out.end_table()
1.363 -
1.364 -class TableCell(Container):
1.365 -
1.366 - "A table cell."
1.367 -
1.368 - def __init__(self, nodes, attrs=None):
1.369 - Container.__init__(self, nodes)
1.370 - self.attrs = attrs
1.371 -
1.372 - def __repr__(self):
1.373 - return "TableCell(%r, %r)" % (self.nodes, self.attrs)
1.374 -
1.375 - def prettyprint(self, indent=""):
1.376 - l = ["%sTableCell:" % indent]
1.377 - return self._prettyprint(l, indent)
1.378 -
1.379 - def to_string(self, out):
1.380 - out.start_table_cell(self.attrs)
1.381 - for node in self.nodes:
1.382 - if node is not self.attrs:
1.383 - node.to_string(out)
1.384 - out.end_table_cell()
1.385 -
1.386 -class TableRow(Container):
1.387 -
1.388 - "A table row."
1.389 -
1.390 - def __init__(self, nodes, trailing=""):
1.391 - Container.__init__(self, nodes)
1.392 - self.trailing = trailing
1.393 -
1.394 - def __repr__(self):
1.395 - return "TableRow(%r, %r)" % (self.nodes, self.trailing)
1.396 -
1.397 - def prettyprint(self, indent=""):
1.398 - l = ["%sTableRow: trailing=%r" % (indent, self.trailing)]
1.399 - return self._prettyprint(l, indent)
1.400 -
1.401 - def to_string(self, out):
1.402 - out.start_table_row()
1.403 - self._to_string(out)
1.404 - out.end_table_row(self.trailing)
1.405 -
1.406 -
1.407 -
1.408 -# Inline nodes with children.
1.409 -
1.410 -class Inline(Container):
1.411 -
1.412 - "Generic inline formatting."
1.413 -
1.414 - def __repr__(self):
1.415 - return "%s(%r)" % (self.__class__.__name__, self.nodes)
1.416 -
1.417 - def prettyprint(self, indent=""):
1.418 - l = ["%s%s" % (indent, self.__class__.__name__)]
1.419 - return self._prettyprint(l, indent)
1.420 -
1.421 -class Larger(Inline):
1.422 -
1.423 - "Larger text."
1.424 -
1.425 - def to_string(self, out):
1.426 - out.start_larger()
1.427 - self._to_string(out)
1.428 - out.end_larger()
1.429 -
1.430 -class Link(Container):
1.431 -
1.432 - "Link details."
1.433 -
1.434 - def __init__(self, nodes, target):
1.435 - Container.__init__(self, nodes)
1.436 - self.target = target
1.437 -
1.438 - def __repr__(self):
1.439 - return "Link(%r, %r)" % (self.nodes, self.target)
1.440 -
1.441 - def prettyprint(self, indent=""):
1.442 - l = ["%sLink: target=%r" % (indent, self.target)]
1.443 - return self._prettyprint(l, indent)
1.444 -
1.445 - def to_string(self, out):
1.446 - out.start_link(self.target)
1.447 - if self.nodes:
1.448 - out.start_linktext()
1.449 - self._to_string(out)
1.450 - out.end_linktext()
1.451 - out.end_link()
1.452 -
1.453 -class Monospace(Inline):
1.454 -
1.455 - "Monospaced text."
1.456 -
1.457 - def to_string(self, out):
1.458 - out.start_monospace()
1.459 - self._to_string(out)
1.460 - out.end_monospace()
1.461 -
1.462 -class Smaller(Inline):
1.463 -
1.464 - "Smaller text."
1.465 -
1.466 - def to_string(self, out):
1.467 - out.start_smaller()
1.468 - self._to_string(out)
1.469 - out.end_smaller()
1.470 -
1.471 -class Strikethrough(Inline):
1.472 -
1.473 - "Crossed-out text."
1.474 -
1.475 - def to_string(self, out):
1.476 - out.start_strikethrough()
1.477 - self._to_string(out)
1.478 - out.end_strikethrough()
1.479 -
1.480 -class Subscript(Inline):
1.481 -
1.482 - "Subscripted text."
1.483 -
1.484 - def to_string(self, out):
1.485 - out.start_subscript()
1.486 - self._to_string(out)
1.487 - out.end_subscript()
1.488 -
1.489 -class Superscript(Inline):
1.490 -
1.491 - "Superscripted text."
1.492 -
1.493 - def to_string(self, out):
1.494 - out.start_superscript()
1.495 - self._to_string(out)
1.496 - out.end_superscript()
1.497 -
1.498 -class Underline(Inline):
1.499 -
1.500 - "Underlined text."
1.501 -
1.502 - def to_string(self, out):
1.503 - out.start_underline()
1.504 - self._to_string(out)
1.505 - out.end_underline()
1.506 -
1.507 -
1.508 -
1.509 -# Nodes without children.
1.510 -
1.511 -class Node:
1.512 -
1.513 - "A document node without children."
1.514 -
1.515 - def empty(self):
1.516 - return False
1.517 -
1.518 -class Break(Node):
1.519 -
1.520 - "A paragraph break."
1.521 -
1.522 - def __repr__(self):
1.523 - return "Break()"
1.524 -
1.525 - def prettyprint(self, indent=""):
1.526 - return "%sBreak" % indent
1.527 -
1.528 - def to_string(self, out):
1.529 - out.break_()
1.530 -
1.531 -class Continuation(Node):
1.532 -
1.533 - "Continuation padding for table content."
1.534 -
1.535 - def __init__(self, text):
1.536 - self.text = text
1.537 -
1.538 - def __repr__(self):
1.539 - return "Continuation(%r)" % self.text
1.540 -
1.541 - def prettyprint(self, indent=""):
1.542 - return "%sContinuation: %r" % (indent, self.text)
1.543 -
1.544 - def to_string(self, out):
1.545 - out.continuation(self.text)
1.546 -
1.547 -class Macro(Node):
1.548 -
1.549 - "Macro details."
1.550 -
1.551 - def __init__(self, name, args):
1.552 - self.name = name
1.553 - self.args = args
1.554 -
1.555 - def __repr__(self):
1.556 - return "Macro(%r, %r)" % (self.name, self.args)
1.557 -
1.558 - def prettyprint(self, indent=""):
1.559 - return "%sMacro: name=%r args=%r" % (indent, self.name, self.args)
1.560 -
1.561 - def to_string(self, out):
1.562 - out.macro(self.name, self.args)
1.563 -
1.564 -class Rule(Node):
1.565 -
1.566 - "A horizontal rule."
1.567 -
1.568 - def __init__(self, length):
1.569 - self.length = length
1.570 -
1.571 - def __repr__(self):
1.572 - return "Rule(%d)" % self.length
1.573 -
1.574 - def prettyprint(self, indent=""):
1.575 - return "%sRule: length=%d" % (indent, self.length)
1.576 -
1.577 - def to_string(self, out):
1.578 - out.rule(self.length)
1.579 -
1.580 -class TableAttr(Node):
1.581 -
1.582 - "A table attribute."
1.583 -
1.584 - def __init__(self, name, value=None, concise=False, quote=None):
1.585 - self.name = name
1.586 - self.value = value
1.587 - self.concise = concise
1.588 - self.quote = quote
1.589 -
1.590 - def __repr__(self):
1.591 - return "TableAttr(%r, %r, %r, %r)" % (self.name, self.value, self.concise, self.quote)
1.592 -
1.593 - def prettyprint(self, indent=""):
1.594 - return "%sTableAttr: name=%r value=%r concise=%r quote=%r" % (indent, self.name, self.value, self.concise, self.quote)
1.595 -
1.596 - def to_string(self, out):
1.597 - out.table_attr(self.name, self.value, self.concise, self.quote)
1.598 -
1.599 -class Text(Node):
1.600 -
1.601 - "A text node."
1.602 -
1.603 - def __init__(self, s):
1.604 - self.s = s
1.605 -
1.606 - def empty(self):
1.607 - return not self.s
1.608 -
1.609 - def multiline(self):
1.610 - return "\n" in self.s
1.611 -
1.612 - def merge(self, text):
1.613 - self.s += text.s
1.614 -
1.615 - def __repr__(self):
1.616 - return "Text(%r)" % self.s
1.617 -
1.618 - def prettyprint(self, indent=""):
1.619 - return "%sText: %r" % (indent, self.s)
1.620 -
1.621 - def to_string(self, out):
1.622 - out.text(self.s)
1.623 -
1.624 -# vim: tabstop=4 expandtab shiftwidth=4