1.1 --- a/moinformat/__init__.py Sat Apr 29 18:20:55 2017 +0200
1.2 +++ b/moinformat/__init__.py Sat Apr 29 23:04:30 2017 +0200
1.3 @@ -19,31 +19,39 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -from moinformat.tree import Block, ListItem, Region, Rule, Text
1.8 +from moinformat.tree import Block, Heading, ListItem, Region, Rule, Text
1.9 import re
1.10
1.11 # Regular expressions.
1.12
1.13 syntax = {
1.14 # Page regions:
1.15 - "regionstart" : (r"((^\s*)([{]{3,}))", re.MULTILINE | re.DOTALL), # {{{...
1.16 - "regionend" : (r"^\s*([}]{3,})", re.MULTILINE | re.DOTALL), # }}}...
1.17 - "header" : (r"#!(.*?)\n", 0), # #! char-excl-nl
1.18 + "regionstart" : r"((^\s*)([{]{3,}))", # {{{...
1.19 + "regionend" : r"^\s*([}]{3,})", # }}}...
1.20 + "header" : r"#!(.*?)\n", # #! char-excl-nl
1.21
1.22 # Region contents:
1.23 - "break" : (r"^(\s*?)\n", re.MULTILINE), # blank line
1.24 - "listitem" : (r"^((\s+)([*]|\d+[.]))", re.MULTILINE), # indent (list-item or number-item)
1.25 - "rule" : (r"(-----*)", 0), # ----...
1.26 + # Line-oriented patterns:
1.27 + "break" : r"^(\s*?)\n", # blank line
1.28 + "heading" : r"^(\s*)(?P<x>=+)(\s+)(?=.*?\s+(?P=x)\s*\n)", # [ws...] =... ws... expecting headingend
1.29 + "listitem" : r"^((\s+)([*]|\d+[.]))", # indent (list-item or number-item)
1.30 +
1.31 + # Region contents:
1.32 + # Inline patterns:
1.33 + "rule" : r"(-----*)", # ----...
1.34 +
1.35 + # Heading contents:
1.36 + "headingend" : r"(\s+)(=+)(\s*\n)", # ws... =... [ws...] nl
1.37
1.38 # List contents:
1.39 - "listitemend" : (r"^", re.MULTILINE), # next line
1.40 + "listitemend" : r"^", # next line
1.41 }
1.42
1.43 # Define patterns for the regular expressions.
1.44
1.45 patterns = {}
1.46 -for name, (value, flags) in syntax.items():
1.47 - patterns[name] = re.compile(value, re.UNICODE | flags)
1.48 +for name, value in syntax.items():
1.49 + patterns[name] = re.compile(value, re.UNICODE | re.MULTILINE)
1.50
1.51
1.52
1.53 @@ -156,7 +164,7 @@
1.54 "Parse the data provided by 'items' to populate a wiki 'region'."
1.55
1.56 new_block(region)
1.57 - parse_region_details(items, region, ["break", "listitem", "regionstart", "regionend", "rule"])
1.58 + parse_region_details(items, region, ["break", "heading", "listitem", "regionstart", "regionend", "rule"])
1.59
1.60 def parse_region_opaque(items, region):
1.61
1.62 @@ -215,11 +223,27 @@
1.63 block.final = False
1.64 new_block(region)
1.65
1.66 -def parse_listitem_end(items, region):
1.67 +def parse_heading(items, region):
1.68 +
1.69 + "Handle a heading."
1.70
1.71 - "Handle the end of a list."
1.72 + start_extra = items.read_match(1)
1.73 + level = len(items.read_match(2))
1.74 + start_pad = items.read_match(3)
1.75 + heading = Heading([], level, start_extra, start_pad)
1.76 + parse_region_details(items, heading, ["headingend"])
1.77 + region.append(heading)
1.78 + new_block(region)
1.79
1.80 - raise StopIteration
1.81 +def parse_heading_end(items, heading):
1.82 +
1.83 + "Handle the end of a heading."
1.84 +
1.85 + level = len(items.read_match(2))
1.86 + if heading.level == level:
1.87 + heading.end_pad = items.read_match(1)
1.88 + heading.end_extra = items.read_match(3)
1.89 + raise StopIteration
1.90
1.91 def parse_listitem(items, region):
1.92
1.93 @@ -230,6 +254,12 @@
1.94 region.append(item)
1.95 new_block(region)
1.96
1.97 +def parse_listitem_end(items, item):
1.98 +
1.99 + "Handle the end of a list."
1.100 +
1.101 + raise StopIteration
1.102 +
1.103 def parse_rule(items, region):
1.104
1.105 "Handle a horizontal rule within 'region'."
1.106 @@ -265,6 +295,8 @@
1.107 handlers = {
1.108 None : end_region,
1.109 "break" : parse_break,
1.110 + "heading" : parse_heading,
1.111 + "headingend" : parse_heading_end,
1.112 "listitemend" : parse_listitem_end,
1.113 "listitem" : parse_listitem,
1.114 "regionstart" : parse_section,
2.1 --- a/moinformat/serialisers.py Sat Apr 29 18:20:55 2017 +0200
2.2 +++ b/moinformat/serialisers.py Sat Apr 29 23:04:30 2017 +0200
2.3 @@ -51,6 +51,12 @@
2.4 if not final:
2.5 self.out("\n")
2.6
2.7 + def start_heading(self, level, extra, pad):
2.8 + self.out(extra + "=" * level + pad)
2.9 +
2.10 + def end_heading(self, level, pad, extra):
2.11 + self.out(pad + "=" * level + extra)
2.12 +
2.13 def start_listitem(self):
2.14 self.out(" *")
2.15
2.16 @@ -92,6 +98,12 @@
2.17 def end_block(self, final):
2.18 self.out("</p>")
2.19
2.20 + def start_heading(self, level, extra, pad):
2.21 + self.out("<h%d>" % level)
2.22 +
2.23 + def end_heading(self, level, pad, extra):
2.24 + self.out("</h%d>" % level)
2.25 +
2.26 def start_listitem(self):
2.27 self.out("<li>")
2.28
3.1 --- a/moinformat/tree.py Sat Apr 29 18:20:55 2017 +0200
3.2 +++ b/moinformat/tree.py Sat Apr 29 23:04:30 2017 +0200
3.3 @@ -140,6 +140,35 @@
3.4 node.to_string(out)
3.5 out.end_block(self.final)
3.6
3.7 +class Heading(Container):
3.8 +
3.9 + "A heading."
3.10 +
3.11 + def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""):
3.12 + Container.__init__(self, nodes)
3.13 + self.level = level
3.14 + self.start_extra = start_extra
3.15 + self.start_pad = start_pad
3.16 + self.end_pad = end_pad
3.17 + self.end_extra = end_extra
3.18 +
3.19 + def __repr__(self):
3.20 + return "Heading(%r, %d, %r, %r, %r, %r)" % (
3.21 + self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)
3.22 +
3.23 + def prettyprint(self, indent=""):
3.24 + l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % (
3.25 + indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)]
3.26 + for node in self.nodes:
3.27 + l.append(node.prettyprint(indent + " "))
3.28 + return "\n".join(l)
3.29 +
3.30 + def to_string(self, out):
3.31 + out.start_heading(self.level, self.start_extra, self.start_pad)
3.32 + for node in self.nodes:
3.33 + node.to_string(out)
3.34 + out.end_heading(self.level, self.end_pad, self.end_extra)
3.35 +
3.36 class ListItem(Container):
3.37
3.38 "A list item."