1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/moinformat/tree.py Sat Apr 29 17:47:03 2017 +0200
1.3 @@ -0,0 +1,184 @@
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 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 + def append(self, node):
1.33 + self.nodes.append(node)
1.34 +
1.35 + append_text = append
1.36 +
1.37 + def empty(self):
1.38 + return not self.nodes
1.39 +
1.40 + def normalise(self):
1.41 +
1.42 + "Combine adjacent text nodes."
1.43 +
1.44 + nodes = self.nodes
1.45 + self.nodes = []
1.46 + text = None
1.47 +
1.48 + for node in nodes:
1.49 +
1.50 + # Open a text node or merge text into an open node.
1.51 +
1.52 + if isinstance(node, Text):
1.53 + if not text:
1.54 + text = node
1.55 + else:
1.56 + text.merge(node)
1.57 +
1.58 + # Close any open text node and append the current node.
1.59 +
1.60 + else:
1.61 + if text:
1.62 + self.append(text)
1.63 + text = None
1.64 + self.append(node)
1.65 +
1.66 + # Add any open text node.
1.67 +
1.68 + if text:
1.69 + self.append(text)
1.70 +
1.71 + def __str__(self):
1.72 + return self.prettyprint()
1.73 +
1.74 + def prettyprint(self, indent=""):
1.75 + pass
1.76 +
1.77 +class Region(Container):
1.78 +
1.79 + "A region of the page."
1.80 +
1.81 + transparent_region_types = ["wiki"]
1.82 +
1.83 + def __init__(self, nodes, level=0, indent=0, type=None):
1.84 + Container.__init__(self, nodes)
1.85 + self.level = level
1.86 + self.indent = indent
1.87 + self.type = type
1.88 +
1.89 + def append(self, node):
1.90 + last = self.nodes and self.nodes[-1]
1.91 + if last and last.empty():
1.92 + self.nodes[-1] = node
1.93 + else:
1.94 + self.nodes.append(node)
1.95 +
1.96 + def append_text(self, s):
1.97 + if self.is_transparent():
1.98 + self.nodes[-1].append(s)
1.99 + else:
1.100 + self.append(s)
1.101 +
1.102 + def have_end(self, s):
1.103 + return self.level and s.startswith("}") and self.level == len(s)
1.104 +
1.105 + def is_transparent(self):
1.106 + return not self.level or self.type in self.transparent_region_types
1.107 +
1.108 + def __repr__(self):
1.109 + return "Region(%r, %r, %r, %r)" % (self.nodes, self.level, self.indent, self.type)
1.110 +
1.111 + def prettyprint(self, indent=""):
1.112 + l = ["%sRegion: level=%d indent=%d type=%s" % (indent, self.level, self.indent, self.type)]
1.113 + for node in self.nodes:
1.114 + l.append(node.prettyprint(indent + " "))
1.115 + return "\n".join(l)
1.116 +
1.117 + def to_string(self, out):
1.118 + out.start_region(self.level, self.indent, self.type)
1.119 + for node in self.nodes:
1.120 + node.to_string(out)
1.121 + out.end_region(self.level, self.indent, self.type)
1.122 +
1.123 +class Block(Container):
1.124 +
1.125 + "A block in the page."
1.126 +
1.127 + def __init__(self, nodes, final=True):
1.128 + Container.__init__(self, nodes)
1.129 + self.final = final
1.130 +
1.131 + def __repr__(self):
1.132 + return "Block(%r)" % self.nodes
1.133 +
1.134 + def prettyprint(self, indent=""):
1.135 + l = ["%sBlock: final=%s" % (indent, self.final)]
1.136 + for node in self.nodes:
1.137 + l.append(node.prettyprint(indent + " "))
1.138 + return "\n".join(l)
1.139 +
1.140 + def to_string(self, out):
1.141 + out.start_block(self.final)
1.142 + for node in self.nodes:
1.143 + node.to_string(out)
1.144 + out.end_block(self.final)
1.145 +
1.146 +class ListItem(Container):
1.147 +
1.148 + "A list item."
1.149 +
1.150 + def __repr__(self):
1.151 + return "ListItem(%r)" % self.nodes
1.152 +
1.153 + def prettyprint(self, indent=""):
1.154 + l = ["%sListItem:" % indent]
1.155 + for node in self.nodes:
1.156 + l.append(node.prettyprint(indent + " "))
1.157 + return "\n".join(l)
1.158 +
1.159 + def to_string(self, out):
1.160 + out.start_listitem()
1.161 + for node in self.nodes:
1.162 + node.to_string(out)
1.163 + out.end_listitem()
1.164 +
1.165 +class Text:
1.166 +
1.167 + "A text node."
1.168 +
1.169 + def __init__(self, s):
1.170 + self.s = s
1.171 +
1.172 + def empty(self):
1.173 + return not self.s
1.174 +
1.175 + def merge(self, text):
1.176 + self.s += text.s
1.177 +
1.178 + def __repr__(self):
1.179 + return "Text(%r)" % self.s
1.180 +
1.181 + def prettyprint(self, indent=""):
1.182 + return "%sText: %r" % (indent, self.s)
1.183 +
1.184 + def to_string(self, out):
1.185 + out.text(self.s)
1.186 +
1.187 +# vim: tabstop=4 expandtab shiftwidth=4