# HG changeset patch # User Paul Boddie # Date 1493334586 -7200 # Node ID c3831bd8835fe151360f71f78d245692968f9aa0 # Parent 0cbb97f958954a7835d01b33203abd78baa86031 Added initial support for list item parsing. diff -r 0cbb97f95895 -r c3831bd8835f moinformat.py --- a/moinformat.py Thu Apr 27 23:42:43 2017 +0200 +++ b/moinformat.py Fri Apr 28 01:09:46 2017 +0200 @@ -32,6 +32,10 @@ # Region contents: "break" : (r"^(\s*?)\n", re.MULTILINE), # blank line + "listitem" : (r"^((\s+)([*]|\d+[.]))", re.MULTILINE), # indent (list-item or number-item) + + # List contents: + "listitemend" : (r"^", re.MULTILINE), # next line } # Define patterns for the regular expressions. @@ -56,6 +60,9 @@ append_text = append + def empty(self): + return not self.nodes + def normalise(self): "Combine adjacent text nodes." @@ -104,6 +111,13 @@ self.level = level self.type = type + def append(self, node): + last = self.nodes and self.nodes[-1] + if last and last.empty(): + self.nodes[-1] = node + else: + self.nodes.append(node) + def append_text(self, s): if self.is_transparent(): self.nodes[-1].append(s) @@ -154,6 +168,26 @@ node.to_string(out) out.end_block(self.final) +class ListItem(Container): + + "A list item." + + def __repr__(self): + return "ListItem(%r)" % self.nodes + + def prettyprint(self, indent=""): + l = ["%sListItem:" % indent] + for node in self.nodes: + l.append(node.prettyprint(indent + " ")) + return "\n".join(l) + + def to_string(self, out): + out.start_listitem() + for node in self.nodes: + node.to_string(out) + out.end_listitem() + + class Text: "A text node." @@ -161,6 +195,9 @@ def __init__(self, s): self.s = s + def empty(self): + return not self.s + def merge(self, text): self.s += text.s @@ -207,6 +244,12 @@ if not final: self.out("\n") + def start_listitem(self): + self.out(" *") + + def end_listitem(self): + pass + def text(self, s): self.out(s) @@ -236,6 +279,12 @@ def end_block(self, final): self.out("

") + def start_listitem(self): + self.out("
  • ") + + def end_listitem(self): + self.out("
  • ") + def text(self, s): self.out(escape(s)) @@ -289,8 +338,10 @@ if self.match: _start, self.pos = self.match.span() - s = self.match.group(1) - return s + try: + return self.match.group(1) + except IndexError: + return "" else: self.pos = len(self.s) return None @@ -343,7 +394,7 @@ "Parse the data provided by 'items' to populate a wiki 'region'." new_block(region) - parse_region_details(items, region, ["break", "regionstart", "regionend"]) + parse_region_details(items, region, ["break", "listitem", "regionstart", "regionend"]) def parse_region_opaque(items, region): @@ -402,6 +453,21 @@ block.final = False new_block(region) +def parse_listitem_end(items, region): + + "Handle the end of a list." + + raise StopIteration + +def parse_listitem(items, region): + + "Handle a list item marker within 'region'." + + item = ListItem([]) + parse_region_details(items, item, ["listitemend"]) + region.append(item) + new_block(region) + def parse_section(items, region): "Handle the start of a new section within 'region'." @@ -427,6 +493,8 @@ handlers = { None : end_region, "break" : parse_break, + "listitemend" : parse_listitem_end, + "listitem" : parse_listitem, "regionstart" : parse_section, "regionend" : parse_section_end, } diff -r 0cbb97f95895 -r c3831bd8835f tests/test_parser.py --- a/tests/test_parser.py Thu Apr 27 23:42:43 2017 +0200 +++ b/tests/test_parser.py Fri Apr 28 01:09:46 2017 +0200 @@ -12,6 +12,10 @@ End }}}} XXX + + * Item 1 + * Item 2 +XXX """ s1 = """\