# HG changeset patch
# User Paul Boddie
# Date 1493499870 -7200
# Node ID 7825ca4d035797bc70a829e16112667858ef6085
# Parent 575c7b5d97ac7e0c6f249e7fd1c92eda6661be21
Added support for headings.
diff -r 575c7b5d97ac -r 7825ca4d0357 moinformat/__init__.py
--- a/moinformat/__init__.py Sat Apr 29 18:20:55 2017 +0200
+++ b/moinformat/__init__.py Sat Apr 29 23:04:30 2017 +0200
@@ -19,31 +19,39 @@
this program. If not, see .
"""
-from moinformat.tree import Block, ListItem, Region, Rule, Text
+from moinformat.tree import Block, Heading, ListItem, Region, Rule, Text
import re
# Regular expressions.
syntax = {
# Page regions:
- "regionstart" : (r"((^\s*)([{]{3,}))", re.MULTILINE | re.DOTALL), # {{{...
- "regionend" : (r"^\s*([}]{3,})", re.MULTILINE | re.DOTALL), # }}}...
- "header" : (r"#!(.*?)\n", 0), # #! char-excl-nl
+ "regionstart" : r"((^\s*)([{]{3,}))", # {{{...
+ "regionend" : r"^\s*([}]{3,})", # }}}...
+ "header" : r"#!(.*?)\n", # #! char-excl-nl
# Region contents:
- "break" : (r"^(\s*?)\n", re.MULTILINE), # blank line
- "listitem" : (r"^((\s+)([*]|\d+[.]))", re.MULTILINE), # indent (list-item or number-item)
- "rule" : (r"(-----*)", 0), # ----...
+ # Line-oriented patterns:
+ "break" : r"^(\s*?)\n", # blank line
+ "heading" : r"^(\s*)(?P=+)(\s+)(?=.*?\s+(?P=x)\s*\n)", # [ws...] =... ws... expecting headingend
+ "listitem" : r"^((\s+)([*]|\d+[.]))", # indent (list-item or number-item)
+
+ # Region contents:
+ # Inline patterns:
+ "rule" : r"(-----*)", # ----...
+
+ # Heading contents:
+ "headingend" : r"(\s+)(=+)(\s*\n)", # ws... =... [ws...] nl
# List contents:
- "listitemend" : (r"^", re.MULTILINE), # next line
+ "listitemend" : r"^", # next line
}
# Define patterns for the regular expressions.
patterns = {}
-for name, (value, flags) in syntax.items():
- patterns[name] = re.compile(value, re.UNICODE | flags)
+for name, value in syntax.items():
+ patterns[name] = re.compile(value, re.UNICODE | re.MULTILINE)
@@ -156,7 +164,7 @@
"Parse the data provided by 'items' to populate a wiki 'region'."
new_block(region)
- parse_region_details(items, region, ["break", "listitem", "regionstart", "regionend", "rule"])
+ parse_region_details(items, region, ["break", "heading", "listitem", "regionstart", "regionend", "rule"])
def parse_region_opaque(items, region):
@@ -215,11 +223,27 @@
block.final = False
new_block(region)
-def parse_listitem_end(items, region):
+def parse_heading(items, region):
+
+ "Handle a heading."
- "Handle the end of a list."
+ start_extra = items.read_match(1)
+ level = len(items.read_match(2))
+ start_pad = items.read_match(3)
+ heading = Heading([], level, start_extra, start_pad)
+ parse_region_details(items, heading, ["headingend"])
+ region.append(heading)
+ new_block(region)
- raise StopIteration
+def parse_heading_end(items, heading):
+
+ "Handle the end of a heading."
+
+ level = len(items.read_match(2))
+ if heading.level == level:
+ heading.end_pad = items.read_match(1)
+ heading.end_extra = items.read_match(3)
+ raise StopIteration
def parse_listitem(items, region):
@@ -230,6 +254,12 @@
region.append(item)
new_block(region)
+def parse_listitem_end(items, item):
+
+ "Handle the end of a list."
+
+ raise StopIteration
+
def parse_rule(items, region):
"Handle a horizontal rule within 'region'."
@@ -265,6 +295,8 @@
handlers = {
None : end_region,
"break" : parse_break,
+ "heading" : parse_heading,
+ "headingend" : parse_heading_end,
"listitemend" : parse_listitem_end,
"listitem" : parse_listitem,
"regionstart" : parse_section,
diff -r 575c7b5d97ac -r 7825ca4d0357 moinformat/serialisers.py
--- a/moinformat/serialisers.py Sat Apr 29 18:20:55 2017 +0200
+++ b/moinformat/serialisers.py Sat Apr 29 23:04:30 2017 +0200
@@ -51,6 +51,12 @@
if not final:
self.out("\n")
+ def start_heading(self, level, extra, pad):
+ self.out(extra + "=" * level + pad)
+
+ def end_heading(self, level, pad, extra):
+ self.out(pad + "=" * level + extra)
+
def start_listitem(self):
self.out(" *")
@@ -92,6 +98,12 @@
def end_block(self, final):
self.out("
")
+ def start_heading(self, level, extra, pad):
+ self.out("" % level)
+
+ def end_heading(self, level, pad, extra):
+ self.out("" % level)
+
def start_listitem(self):
self.out("")
diff -r 575c7b5d97ac -r 7825ca4d0357 moinformat/tree.py
--- a/moinformat/tree.py Sat Apr 29 18:20:55 2017 +0200
+++ b/moinformat/tree.py Sat Apr 29 23:04:30 2017 +0200
@@ -140,6 +140,35 @@
node.to_string(out)
out.end_block(self.final)
+class Heading(Container):
+
+ "A heading."
+
+ def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""):
+ Container.__init__(self, nodes)
+ self.level = level
+ self.start_extra = start_extra
+ self.start_pad = start_pad
+ self.end_pad = end_pad
+ self.end_extra = end_extra
+
+ def __repr__(self):
+ return "Heading(%r, %d, %r, %r, %r, %r)" % (
+ self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)
+
+ def prettyprint(self, indent=""):
+ l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % (
+ indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)]
+ for node in self.nodes:
+ l.append(node.prettyprint(indent + " "))
+ return "\n".join(l)
+
+ def to_string(self, out):
+ out.start_heading(self.level, self.start_extra, self.start_pad)
+ for node in self.nodes:
+ node.to_string(out)
+ out.end_heading(self.level, self.end_pad, self.end_extra)
+
class ListItem(Container):
"A list item."
diff -r 575c7b5d97ac -r 7825ca4d0357 tests/test_parser.py
--- a/tests/test_parser.py Sat Apr 29 18:20:55 2017 +0200
+++ b/tests/test_parser.py Sat Apr 29 23:04:30 2017 +0200
@@ -59,6 +59,17 @@
also still a rule----
EOF""")
+sl.append("""\
+= Level 1 =
+Text
+ == Level 2 Heading ==
+Text
+Not == a heading ==
+== Not a heading == either
+= Mismatched heading ==
+== Another mismatched heading =
+""")
+
dl = map(parse, sl)
nl = map(serialise, dl)