1.1 --- a/moinformat.py Thu Apr 27 23:42:43 2017 +0200
1.2 +++ b/moinformat.py Fri Apr 28 01:09:46 2017 +0200
1.3 @@ -32,6 +32,10 @@
1.4
1.5 # Region contents:
1.6 "break" : (r"^(\s*?)\n", re.MULTILINE), # blank line
1.7 + "listitem" : (r"^((\s+)([*]|\d+[.]))", re.MULTILINE), # indent (list-item or number-item)
1.8 +
1.9 + # List contents:
1.10 + "listitemend" : (r"^", re.MULTILINE), # next line
1.11 }
1.12
1.13 # Define patterns for the regular expressions.
1.14 @@ -56,6 +60,9 @@
1.15
1.16 append_text = append
1.17
1.18 + def empty(self):
1.19 + return not self.nodes
1.20 +
1.21 def normalise(self):
1.22
1.23 "Combine adjacent text nodes."
1.24 @@ -104,6 +111,13 @@
1.25 self.level = level
1.26 self.type = type
1.27
1.28 + def append(self, node):
1.29 + last = self.nodes and self.nodes[-1]
1.30 + if last and last.empty():
1.31 + self.nodes[-1] = node
1.32 + else:
1.33 + self.nodes.append(node)
1.34 +
1.35 def append_text(self, s):
1.36 if self.is_transparent():
1.37 self.nodes[-1].append(s)
1.38 @@ -154,6 +168,26 @@
1.39 node.to_string(out)
1.40 out.end_block(self.final)
1.41
1.42 +class ListItem(Container):
1.43 +
1.44 + "A list item."
1.45 +
1.46 + def __repr__(self):
1.47 + return "ListItem(%r)" % self.nodes
1.48 +
1.49 + def prettyprint(self, indent=""):
1.50 + l = ["%sListItem:" % indent]
1.51 + for node in self.nodes:
1.52 + l.append(node.prettyprint(indent + " "))
1.53 + return "\n".join(l)
1.54 +
1.55 + def to_string(self, out):
1.56 + out.start_listitem()
1.57 + for node in self.nodes:
1.58 + node.to_string(out)
1.59 + out.end_listitem()
1.60 +
1.61 +
1.62 class Text:
1.63
1.64 "A text node."
1.65 @@ -161,6 +195,9 @@
1.66 def __init__(self, s):
1.67 self.s = s
1.68
1.69 + def empty(self):
1.70 + return not self.s
1.71 +
1.72 def merge(self, text):
1.73 self.s += text.s
1.74
1.75 @@ -207,6 +244,12 @@
1.76 if not final:
1.77 self.out("\n")
1.78
1.79 + def start_listitem(self):
1.80 + self.out(" *")
1.81 +
1.82 + def end_listitem(self):
1.83 + pass
1.84 +
1.85 def text(self, s):
1.86 self.out(s)
1.87
1.88 @@ -236,6 +279,12 @@
1.89 def end_block(self, final):
1.90 self.out("</p>")
1.91
1.92 + def start_listitem(self):
1.93 + self.out("<li>")
1.94 +
1.95 + def end_listitem(self):
1.96 + self.out("</li>")
1.97 +
1.98 def text(self, s):
1.99 self.out(escape(s))
1.100
1.101 @@ -289,8 +338,10 @@
1.102
1.103 if self.match:
1.104 _start, self.pos = self.match.span()
1.105 - s = self.match.group(1)
1.106 - return s
1.107 + try:
1.108 + return self.match.group(1)
1.109 + except IndexError:
1.110 + return ""
1.111 else:
1.112 self.pos = len(self.s)
1.113 return None
1.114 @@ -343,7 +394,7 @@
1.115 "Parse the data provided by 'items' to populate a wiki 'region'."
1.116
1.117 new_block(region)
1.118 - parse_region_details(items, region, ["break", "regionstart", "regionend"])
1.119 + parse_region_details(items, region, ["break", "listitem", "regionstart", "regionend"])
1.120
1.121 def parse_region_opaque(items, region):
1.122
1.123 @@ -402,6 +453,21 @@
1.124 block.final = False
1.125 new_block(region)
1.126
1.127 +def parse_listitem_end(items, region):
1.128 +
1.129 + "Handle the end of a list."
1.130 +
1.131 + raise StopIteration
1.132 +
1.133 +def parse_listitem(items, region):
1.134 +
1.135 + "Handle a list item marker within 'region'."
1.136 +
1.137 + item = ListItem([])
1.138 + parse_region_details(items, item, ["listitemend"])
1.139 + region.append(item)
1.140 + new_block(region)
1.141 +
1.142 def parse_section(items, region):
1.143
1.144 "Handle the start of a new section within 'region'."
1.145 @@ -427,6 +493,8 @@
1.146 handlers = {
1.147 None : end_region,
1.148 "break" : parse_break,
1.149 + "listitemend" : parse_listitem_end,
1.150 + "listitem" : parse_listitem,
1.151 "regionstart" : parse_section,
1.152 "regionend" : parse_section_end,
1.153 }