View Issue Details

IDProjectCategoryView StatusLast Update
0000043CAT-SOOP[All Projects] Generalpublic2018-11-14 22:04
ReporterhartzAssigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version13.0.0 
Target VersionFixed in Version 
Summary0000043: showhide tags don't allow arbitrary markdown
DescriptionIn particular, showhide tags are confused by blank lines (which cause Markdown to put in

tags, which cause the HTML parser to get confused and prematurely end the showhide tag.
TagsNo tags attached.
Attach Tags

Activities

kade

2018-11-14 21:55

reporter   ~0000029

Here's the modified patch.
– Allows nesting of <comment> tags
– Allows nesting of <showhide> tags
– Blank lines in <showhide> tags are converted to
tags, to prevent premature ejection

This patch closes @0000039.

showhide.patch (3,875 bytes)
diff -r 6e1ca6a0c651 catsoop/language.py
--- a/catsoop/language.py	Wed Nov 14 16:56:08 2018 -0500
+++ b/catsoop/language.py	Wed Nov 14 21:52:16 2018 -0500
@@ -163,27 +163,63 @@
 
 
 def _md(x):
-    replace_LF_with_br = ["showhide"]
+    def __md(x):
+        replace_LF_with_br = ["showhide",]
+        custom_tags        = ["comment",]\
+                           + replace_LF_with_br
+
+        blank_lines = re.compile(r'\n(\n+)', re.MULTILINE)
+        def br(m):
+            return "\n" + "\n".join(["<br/>"]*len(m.group(1))) + "\n"
+
+        for tag_type in custom_tags:
+            opening_expr = re.compile(r'<\s*%s(\s+[^>]+)?\s*>' % tag_type)
+            closing_expr = re.compile(r'</\s*%s\s*>' % tag_type)
+            # find all opening and closing tags
+            opening_tags = [(m, +1) for m in re.finditer(opening_expr, x)]
+            closing_tags = [(m, -1) for m in re.finditer(closing_expr, x)]
+            tags = sorted(opening_tags + closing_tags, key=(lambda t: t[0].start()))
+
+            #import html
+            #return "\n</br>\n".join(html.escape(str(t[0])) for t in tags)
 
-    blank_lines = re.compile(r'\n(\n+)', re.MULTILINE)
-    def br(m):
-        return "\n" + "\n".join(["<br/>"]*len(m.group(1))) + "\n"
-    for tag in replace_LF_with_br:
-        opening = re.compile(r'<\s*%s(\s+[^>]+)?\s*>' % tag)
-        closing = re.compile(r'</\s*%s\s*>' % tag)
-        this_op = re.search(opening, x)
-        while this_op:
-            this_cl = closing.search(x, this_op.start())
-            if this_cl is None:
-                break
-            interior = x[this_op.end():this_cl.start()]
-            repl = re.sub(blank_lines, br, interior)
-            len_change = len(repl) - len(interior)
-            x = x[:this_op.end()] + repl + x[this_cl.start():]
-            this_op = opening.search(x, this_cl.end()+len_change)
+            while len(tags) > 0:
+                # check for an unexpected closing tag
+                if tags[0][0].re == closing_expr:
+                    raise RuntimeError('Unexpected closing tag %s' % tags[0][0].group(0))
+                # find paired tag
+                pre_start = tags[0][0].start()
+                start = tags[0][0].end()
+                depth = 0
+                for t in tags:
+                    depth += t[1]
+                    if depth == 0:
+                        stop = t[0].start()
+                        post_stop = t[0].end()
+                        break
+                if depth != 0:
+                    raise RuntimeError('Unmatched tag %s' % tags[0][0].group(0))
+                # do the thing
+                if tag_type == "comment":
+                    x = x[:pre_start] + x[post_stop:]
+                    len_change = post_stop - pre_start
+
+                elif tag_type in replace_LF_with_br:
+                    interior = x[start:stop]
+                    repl = re.sub(blank_lines, br, interior)
+                    repl = __md(repl)
+                    x = x[:start] + repl + x[stop:]
+                    len_change = len(repl) - len(interior)
+
+                pickup = post_stop + len_change
+                opening_tags = [(m, +1) for m in opening_expr.finditer(x, pickup)]
+                closing_tags = [(m, -1) for m in closing_expr.finditer(x, pickup)]
+                tags = sorted(opening_tags + closing_tags, key=(lambda t: t[0].start()))
+
+        return x
 
     o = markdown.markdown(
-        x,
+        __md(x),
         extensions=[
             tables.TableExtension(),
             fenced_code.FencedCodeExtension(),
@@ -212,7 +248,8 @@
     """
     text = context["cs_content"]
 
-    text = re.sub(_environment_matcher("comment"), "", text)
+    # Now handled by `_md`
+    #text = re.sub(_environment_matcher("comment"), "", text)
 
     text = _md_format_string(context, text, False)
 
showhide.patch (3,875 bytes)

kade

2018-11-14 21:57

reporter   ~0000030

Sorry, that diff is relative to my branch. Let me make a new one.

kade

2018-11-14 22:04

reporter   ~0000031

Here's the right one.
Here's the modified patch.
– Allows nesting of <comment> tags
– Allows nesting of <showhide> tags
– Blank lines in <showhide> tags are converted to
tags, to prevent premature ejection
– The <showhide> tag now supports an attribute "label" to change the text of the button

This patch closes @0000039.

showhide-2.patch (3,650 bytes)
diff -r c035371f7982 catsoop/language.py
--- a/catsoop/language.py	Mon Oct 22 09:28:51 2018 -0400
+++ b/catsoop/language.py	Wed Nov 14 22:02:49 2018 -0500
@@ -163,8 +163,63 @@
 
 
 def _md(x):
+    def __md(x):
+        replace_LF_with_br = ["showhide",]
+        custom_tags        = ["comment",]\
+                           + replace_LF_with_br
+
+        blank_lines = re.compile(r'\n(\n+)', re.MULTILINE)
+        def br(m):
+            return "\n" + "\n".join(["<br/>"]*len(m.group(1))) + "\n"
+
+        for tag_type in custom_tags:
+            opening_expr = re.compile(r'<\s*%s(\s+[^>]+)?\s*>' % tag_type)
+            closing_expr = re.compile(r'</\s*%s\s*>' % tag_type)
+            # find all opening and closing tags
+            opening_tags = [(m, +1) for m in re.finditer(opening_expr, x)]
+            closing_tags = [(m, -1) for m in re.finditer(closing_expr, x)]
+            tags = sorted(opening_tags + closing_tags, key=(lambda t: t[0].start()))
+
+            #import html
+            #return "\n</br>\n".join(html.escape(str(t[0])) for t in tags)
+
+            while len(tags) > 0:
+                # check for an unexpected closing tag
+                if tags[0][0].re == closing_expr:
+                    raise RuntimeError('Unexpected closing tag %s' % tags[0][0].group(0))
+                # find paired tag
+                pre_start = tags[0][0].start()
+                start = tags[0][0].end()
+                depth = 0
+                for t in tags:
+                    depth += t[1]
+                    if depth == 0:
+                        stop = t[0].start()
+                        post_stop = t[0].end()
+                        break
+                if depth != 0:
+                    raise RuntimeError('Unmatched tag %s' % tags[0][0].group(0))
+                # do the thing
+                if tag_type == "comment":
+                    x = x[:pre_start] + x[post_stop:]
+                    len_change = post_stop - pre_start
+
+                elif tag_type in replace_LF_with_br:
+                    interior = x[start:stop]
+                    repl = re.sub(blank_lines, br, interior)
+                    repl = __md(repl)
+                    x = x[:start] + repl + x[stop:]
+                    len_change = len(repl) - len(interior)
+
+                pickup = post_stop + len_change
+                opening_tags = [(m, +1) for m in opening_expr.finditer(x, pickup)]
+                closing_tags = [(m, -1) for m in closing_expr.finditer(x, pickup)]
+                tags = sorted(opening_tags + closing_tags, key=(lambda t: t[0].start()))
+
+        return x
+
     o = markdown.markdown(
-        x,
+        __md(x),
         extensions=[
             tables.TableExtension(),
             fenced_code.FencedCodeExtension(),
@@ -193,7 +248,8 @@
     """
     text = context["cs_content"]
 
-    text = re.sub(_environment_matcher("comment"), "", text)
+    # Now handled by `_md`
+    #text = re.sub(_environment_matcher("comment"), "", text)
 
     text = _md_format_string(context, text, False)
 
@@ -779,8 +835,13 @@
         button = tree.new_tag(
             "button",
             onclick="if(this.nextSibling.style.display === 'none'){this.nextSibling.style.display = 'block';}else{this.nextSibling.style.display = 'none';}",
+            style="font-size:0.85em;",
         )
-        button.string = "Show/Hide"
+        if "label" in i.attrs:
+            button.string = i.attrs["label"]
+            del i.attrs["label"]
+        else:
+            button.string = "Show/Hide"
         i.insert_before(button)
 
     # custom URL handling in img, a, script, link
showhide-2.patch (3,650 bytes)

Add Note

View Status
Note
Upload Files
Maximum size: 2,097 KB

Drop files here to upload (or click)

Issue History

Date Modified Username Field Change
2018-11-07 12:14 hartz New Issue
2018-11-14 21:55 kade File Added: showhide.patch
2018-11-14 21:55 kade Note Added: 0000029
2018-11-14 21:57 kade Note Added: 0000030
2018-11-14 22:04 kade File Added: showhide-2.patch
2018-11-14 22:04 kade Note Added: 0000031