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 append_text = append 33 34 def empty(self): 35 return not self.nodes 36 37 def normalise(self): 38 39 "Combine adjacent text nodes." 40 41 nodes = self.nodes 42 self.nodes = [] 43 text = None 44 45 for node in nodes: 46 47 # Open a text node or merge text into an open node. 48 49 if isinstance(node, Text): 50 if not text: 51 text = node 52 else: 53 text.merge(node) 54 55 # Close any open text node and append the current node. 56 57 else: 58 if text: 59 self.append(text) 60 text = None 61 self.append(node) 62 63 # Add any open text node. 64 65 if text: 66 self.append(text) 67 68 def __str__(self): 69 return self.prettyprint() 70 71 def prettyprint(self, indent=""): 72 pass 73 74 class Region(Container): 75 76 "A region of the page." 77 78 transparent_region_types = ["wiki"] 79 80 def __init__(self, nodes, level=0, indent=0, type=None): 81 Container.__init__(self, nodes) 82 self.level = level 83 self.indent = indent 84 self.type = type 85 86 def append(self, node): 87 last = self.nodes and self.nodes[-1] 88 if last and last.empty(): 89 self.nodes[-1] = node 90 else: 91 self.nodes.append(node) 92 93 def append_text(self, s): 94 if self.is_transparent(): 95 self.nodes[-1].append(s) 96 else: 97 self.append(s) 98 99 def have_end(self, s): 100 return self.level and s.startswith("}") and self.level == len(s) 101 102 def is_transparent(self): 103 return not self.level or self.type in self.transparent_region_types 104 105 def __repr__(self): 106 return "Region(%r, %r, %r, %r)" % (self.nodes, self.level, self.indent, self.type) 107 108 def prettyprint(self, indent=""): 109 l = ["%sRegion: level=%d indent=%d type=%s" % (indent, self.level, self.indent, self.type)] 110 for node in self.nodes: 111 l.append(node.prettyprint(indent + " ")) 112 return "\n".join(l) 113 114 def to_string(self, out): 115 out.start_region(self.level, self.indent, self.type) 116 for node in self.nodes: 117 node.to_string(out) 118 out.end_region(self.level, self.indent, self.type) 119 120 class Block(Container): 121 122 "A block in the page." 123 124 def __init__(self, nodes, final=True): 125 Container.__init__(self, nodes) 126 self.final = final 127 128 def __repr__(self): 129 return "Block(%r)" % self.nodes 130 131 def prettyprint(self, indent=""): 132 l = ["%sBlock: final=%s" % (indent, self.final)] 133 for node in self.nodes: 134 l.append(node.prettyprint(indent + " ")) 135 return "\n".join(l) 136 137 def to_string(self, out): 138 out.start_block(self.final) 139 for node in self.nodes: 140 node.to_string(out) 141 out.end_block(self.final) 142 143 class ListItem(Container): 144 145 "A list item." 146 147 def __repr__(self): 148 return "ListItem(%r)" % self.nodes 149 150 def prettyprint(self, indent=""): 151 l = ["%sListItem:" % indent] 152 for node in self.nodes: 153 l.append(node.prettyprint(indent + " ")) 154 return "\n".join(l) 155 156 def to_string(self, out): 157 out.start_listitem() 158 for node in self.nodes: 159 node.to_string(out) 160 out.end_listitem() 161 162 class Text: 163 164 "A text node." 165 166 def __init__(self, s): 167 self.s = s 168 169 def empty(self): 170 return not self.s 171 172 def merge(self, text): 173 self.s += text.s 174 175 def __repr__(self): 176 return "Text(%r)" % self.s 177 178 def prettyprint(self, indent=""): 179 return "%sText: %r" % (indent, self.s) 180 181 def to_string(self, out): 182 out.text(self.s) 183 184 # vim: tabstop=4 expandtab shiftwidth=4