changeset 2485:c2bd1f3dcd4b

Recursive block quote parsing
author Sam Toyer <sam@qxcv.net>
date Fri, 24 Jun 2016 14:42:58 +1000
parents 38c292be7040
children c022854d3895
files hatta/parser.py tests/test_hatta.py tests/test_parser.py
diffstat 3 files changed, 60 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/hatta/parser.py	Sun Apr 17 12:58:17 2016 -0400
+++ b/hatta/parser.py	Fri Jun 24 14:42:58 2016 +1000
@@ -219,19 +219,18 @@
                 yield ret
             links[:] = []
 
-    def parse(self):
+    def parse(self, enumerated_lines=None):
         """Parse a list of lines of wiki markup, yielding HTML for it."""
 
-        self.headings = {}
-        self.stack = []
-        self.line_no = 0
+        if enumerated_lines is None:
+            enumerated_lines = self.enumerated_lines
 
         def key(enumerated_line):
             line_no, line = enumerated_line
             name, params = self.block_rules.match_one(line)
             return name or "_block_paragraph"
 
-        for kind, block in itertools.groupby(self.enumerated_lines, key):
+        for kind, block in itertools.groupby(enumerated_lines, key):
             func = getattr(self, kind)
             for part in func(block):
                 yield part
@@ -546,32 +545,18 @@
 
     @block_rules(quote_pat, 80)
     def _block_quote(self, block):
-        level = 0
-        in_p = False
-        for self.line_no, line in block:
-            nest = len(self.quote_re.match(line).group(0).strip())
-            if nest == level:
-                yield u'\n'
-            while nest > level:
-                if in_p:
-                    yield '%s</p>' % self.pop_to("")
-                    in_p = False
-                yield '<blockquote>'
-                level += 1
-            while nest < level:
-                if in_p:
-                    yield '%s</p>' % self.pop_to("")
-                    in_p = False
-                yield '</blockquote>'
-                level -= 1
-            content = line.lstrip().lstrip('>').strip()
-            if not in_p:
-                yield '<p id="line_%d">' % self.line_no
-                in_p = True
-            yield u"".join(self.parse_line(content))
-        if in_p:
-            yield '%s</p>' % self.pop_to("")
-        yield '</blockquote>' * level
+        yield '<blockquote>'
+
+        def remove_lead(enumerated_line):
+            line_no, line = enumerated_line
+            stripped = line.lstrip()[1:].lstrip()
+            return (line_no, stripped)
+
+        enumerated_lines = map(remove_lead, block)
+        for content in self.parse(enumerated_lines):
+            yield content
+
+        yield '</blockquote>'
 
     @block_rules(ur"^<<<<<<< local\s*$", 30)
     def _block_conflict(self, block):
--- a/tests/test_hatta.py	Sun Apr 17 12:58:17 2016 -0400
+++ b/tests/test_hatta.py	Fri Jun 24 14:42:58 2016 +1000
@@ -138,12 +138,17 @@
             return u"<a></a>"
         def image(addr, label=None, class_=None, image=None, alt=None):
             return u"<img>"
-        return u''.join(parser(text.split('\n'), link, image))
+        lines = '\n\r'.join(text.split('\n')).split('\r')
+        return u''.join(parser(lines, link, image))
 
 
     test_cases = {
 u"""hello world""": u"""<p id="line_0">hello world</p>""",
 #--------------------------------------------------------------------
+u"""hello
+world""": u"""<p id="line_0">hello
+world</p>""",
+#--------------------------------------------------------------------
 u"""{{{
 some code
 more
@@ -152,7 +157,8 @@
 {{{
 more code
 }}}""": u"""<pre class="code" id="line_1">some code
-more</pre><p id="line_4">some text</p><pre class="code" id="line_6">more code</pre>""",
+more</pre><p id="line_4">some text
+</p><pre class="code" id="line_6">more code</pre>""",
 #--------------------------------------------------------------------
 u"""{{{#!python
 some code
@@ -162,16 +168,20 @@
 {{{#!bash
 more code
 }}}""": u"""<div class="highlight"><pre id="line_1">some code
-more</pre></div><p id="line_4">some text</p><div class="highlight"><pre id="line_6">more code</pre></div>""",
+more</pre></div><p id="line_4">some text
+</p><div class="highlight"><pre id="line_6">more code</pre></div>""",
 #--------------------------------------------------------------------
 u"""Here's a quote:
 > Here is
 > another //quote//:
 >> A quote **within
 >> a quote
-normal text""": u"""<p id="line_0">Here's a quote:</p><blockquote><p id="line_1">Here is
-another <i>quote</i>:</p><blockquote><p id="line_3">A quote <b>within
-a quote</b></p></blockquote></blockquote><p id="line_5">normal text</p>""",
+normal text""": u"""<p id="line_0">Here's a quote:
+</p><blockquote><p id="line_1">Here is
+another <i>quote</i>:
+</p><blockquote><p id="line_3">A quote <b>within
+a quote
+</b></p></blockquote></blockquote><p id="line_5">normal text</p>""",
 #--------------------------------------------------------------------
 u"""* sample list
 ** sublist
--- a/tests/test_parser.py	Sun Apr 17 12:58:17 2016 -0400
+++ b/tests/test_parser.py	Fri Jun 24 14:42:58 2016 +1000
@@ -397,3 +397,31 @@
         html = parse(u'This is ##monotyped## text.')
         assert html == """<p id="line_0">This is <tt>monotyped</tt> text.</p>"""
 
+    def test_inline_quote(self):
+        html = parse(u'> //Simple// quote with\n> one paragraph')
+        assert html == """
+            <blockquote>
+                <p id="line_0"><i>Simple</i> quote with\none paragraph</p>
+            </blockquote>
+        """
+
+    def test_block_quote(self):
+        html = parse(u'> //Complex// quote\n> with\n>\n> = extra markup')
+        assert html == """
+            <blockquote>
+                <p id="line_0"><i>Complex</i> quote\nwith</p>
+                <a name="head-1"></a><h1 id="line_3">extra markup</h1>
+            </blockquote>
+        """
+
+    def test_nested_quotes(self):
+        html = parse(u'> Outer quote\n>> Inner quote')
+        assert html == """
+            <blockquote>
+                <p id="line_0">Outer quote</p>
+                <blockquote>
+                    <p id="line_1">Inner quote</p>
+                </blockquote>
+            </blockquote>
+        """
+