lib/markdown2: introduce markdown rendering to markdown
authorAlexandre Terrasa <alexandre@moz-code.org>
Tue, 29 May 2018 23:55:11 +0000 (19:55 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Wed, 20 Jun 2018 23:11:18 +0000 (19:11 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

lib/markdown2/markdown_md_rendering.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_md.nit [new file with mode: 0644]

diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit
new file mode 100644 (file)
index 0000000..02439e1
--- /dev/null
@@ -0,0 +1,376 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Markdown rendering of Markdown documents
+module markdown_md_rendering
+
+import markdown_rendering
+
+# Markdown document renderer to Markdown
+class MarkdownRenderer
+       super MdRenderer
+
+       # Markdown output under construction
+       private var md: Buffer is noinit
+
+       # Render `node` as Markdown
+       redef fun render(node) do
+               reset
+               enter_visit(node)
+               return md.write_to_string
+       end
+
+       redef fun visit(node) do node.render_md(self)
+
+       # Reset internal state
+       fun reset do
+               md = new Buffer
+       end
+
+       # Current indentation level
+       private var indent = 0
+
+       # Are we currently in a blockquote?
+       var in_quote = 0
+
+       # Add a `md` string to the output
+       fun add_raw(md: String) do self.md.append(md)
+
+       # Add a blank line to the output
+       fun add_line do add_raw "\n"
+
+       # Add an indentation depending on `ident` level
+       fun add_indent do
+               add_raw " " * indent
+       end
+end
+
+private class TextLengthVisitor
+       super MdVisitor
+
+       var length = 0
+
+       redef fun visit(node) do node.process_len(self)
+end
+
+redef class MdNode
+
+       # Render `self` as Markdown
+       fun render_md(v: MarkdownRenderer) do visit_all(v)
+
+       private fun process_len(v: TextLengthVisitor) do visit_all(v)
+end
+
+redef class MdDocument
+       redef fun render_md(v) do
+               var node = first_child
+               while node != null do
+                       v.enter_visit(node)
+                       node = node.next
+                       if node != null then
+                               v.add_line
+                       end
+               end
+       end
+end
+
+# Blocks
+
+redef class MdBlockQuote
+       redef fun render_md(v) do
+               v.in_quote += 1
+               var node = first_child
+               while node != null do
+                       v.add_indent
+                       v.add_raw "> "
+                       v.enter_visit(node)
+                       node = node.next
+               end
+               v.in_quote -= 1
+       end
+end
+
+redef class MdIndentedCodeBlock
+       redef fun render_md(v) do
+               var literal = self.literal
+               if literal == null then return
+
+               var lines = literal.split("\n")
+               for i in [0..lines.length[ do
+                       if i == lines.length - 1 then continue
+                       var line = lines[i]
+                       if line.is_empty then
+                               v.add_raw "\n"
+                       else
+                               v.add_indent
+                               if use_tabs then
+                                       v.add_raw "\t"
+                               else
+                                       v.add_raw " " * 4
+                               end
+                               v.add_raw line
+                               v.add_line
+                       end
+               end
+       end
+end
+
+redef class MdFencedCodeBlock
+       redef fun render_md(v) do
+               var info = self.info
+               v.add_indent
+               v.add_raw fence_char.to_s * fence_length
+               v.add_raw info or else ""
+               for line in (literal or else "").split("\n") do
+                       v.add_line
+                       if not line.is_empty then
+                               v.add_indent
+                       end
+                       v.add_raw line
+               end
+               v.add_indent
+               v.add_raw fence_char.to_s * fence_length
+               v.add_line
+       end
+end
+
+redef class MdHeading
+       redef fun render_md(v) do
+               if is_setext then
+                       visit_all(v)
+                       var length_visitor = new TextLengthVisitor
+                       length_visitor.enter_visit(self)
+                       v.add_line
+                       if level == 1 then
+                               v.add_raw "=" * length_visitor.length
+                       else
+                               v.add_raw "-" * length_visitor.length
+                       end
+               else
+                       v.add_raw "#" * level
+                       v.add_raw " "
+                       visit_all(v)
+                       if has_atx_trailing then
+                               v.add_raw " "
+                               v.add_raw "#" * level
+                       end
+               end
+               v.add_line
+       end
+end
+
+redef class MdOrderedList
+       # Children numbering
+       private var md_numbering: Int = start_number is lazy
+end
+
+redef class MdListItem
+       redef fun render_md(v) do
+               var parent = self.parent
+               var is_tight = parent.as(MdListBlock).is_tight
+
+               v.add_indent
+               if parent isa MdUnorderedList then
+                       v.add_raw parent.bullet_marker.to_s
+                       v.indent += 2
+               else if parent isa MdOrderedList then
+                       v.add_raw "{parent.md_numbering}{parent.delimiter.to_s}"
+                       v.indent += 3
+               end
+
+               var node = first_child
+               if node != null then
+                       v.add_raw " "
+               else
+                       v.add_line
+               end
+               while node != null do
+                       v.enter_visit(node)
+                       node = node.next
+                       if node != null and not is_tight then
+                               v.add_line
+                       end
+               end
+
+               if next != null and not is_tight then
+                       v.add_line
+               end
+
+               if parent isa MdUnorderedList then
+                       v.indent -= 2
+               else if parent isa MdOrderedList then
+                       parent.md_numbering += 1
+                       v.indent -= 3
+               end
+       end
+end
+
+redef class MdParagraph
+       redef fun render_md(v) do
+               if not parent isa MdBlockQuote and not parent isa MdListItem or prev != null then
+                       v.add_indent
+               end
+               # if parent isa MdBlockQuote then
+                       # v.add_raw "> "
+                       # var node = first_child
+                       # while node != null do
+                               # v.enter_visit(node)
+                               # if node isa MdSoftLineBreak or node isa MdHardLineBreak then
+                                       # v.add_raw "> "
+                               # end
+                               # node = node.next
+                       # end
+                       # v.add_line
+                       # return
+               # end
+               visit_all(v)
+               v.add_line
+       end
+end
+
+redef class MdThematicBreak
+       redef fun render_md(v) do
+               v.add_raw original_pattern
+               v.add_line
+       end
+end
+
+redef class MdHtmlBlock
+       redef fun render_md(v) do
+               v.add_raw literal or else ""
+               v.add_line
+       end
+end
+
+# Inlines
+
+redef class MdHardLineBreak
+       redef fun render_md(v) do
+               if has_backslash then
+                       v.add_raw "\\"
+               else
+                       v.add_raw "  "
+               end
+               v.add_line
+               v.add_indent
+               v.add_raw "> " * v.in_quote
+       end
+
+       redef fun process_len(v) do
+               super
+               v.length += 1
+       end
+end
+
+redef class MdSoftLineBreak
+       redef fun render_md(v) do
+               v.add_line
+               v.add_indent
+               v.add_raw "> " * v.in_quote
+       end
+
+       redef fun process_len(v) do
+               super
+               v.length += 1
+       end
+end
+
+redef class MdCode
+       redef fun render_md(v) do
+               v.add_raw delimiter
+               v.add_raw literal
+               v.add_raw delimiter
+       end
+
+       redef fun process_len(v) do
+               super
+               v.length += delimiter.length
+       end
+end
+
+redef class MdDelimited
+       redef fun render_md(v) do
+               v.add_raw delimiter
+               visit_all(v)
+               v.add_raw delimiter
+       end
+
+       redef fun process_len(v) do
+               super
+               v.length += delimiter.length * 2
+       end
+end
+
+redef class MdHtmlInline
+       redef fun render_md(v) do
+               v.add_raw literal
+       end
+
+       redef fun process_len(v) do
+               v.length += literal.length
+       end
+end
+
+redef class MdLinkOrImage
+       redef fun render_md(v) do
+               var title = self.title
+               v.add_raw "["
+               visit_all(v)
+               v.add_raw "]"
+               v.add_raw "("
+               if has_brackets then
+                       v.add_raw "<"
+               end
+               v.add_raw destination
+               if has_brackets then
+                       v.add_raw ">"
+               end
+               if title != null and not title.is_empty then
+                       v.add_raw " \""
+                       v.add_raw title.replace("\"", "\\\"")
+                       v.add_raw "\""
+               end
+               v.add_raw ")"
+       end
+end
+
+
+redef class MdImage
+       redef fun render_md(v) do
+               v.add_raw "!"
+               super
+       end
+end
+
+redef class MdLink
+       redef fun render_md(v) do
+               if is_autolink then
+                       v.add_raw "<"
+                       v.add_raw destination
+                       v.add_raw ">"
+                       return
+               end
+               super
+       end
+end
+
+redef class MdText
+       redef fun render_md(v) do
+               v.add_raw literal
+       end
+
+       redef fun process_len(v) do
+               v.length += literal.length
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_md.nit b/lib/markdown2/tests/test_markdown_md.nit
new file mode 100644 (file)
index 0000000..6705a2f
--- /dev/null
@@ -0,0 +1,663 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tests for markdown rendering to markdown
+module test_markdown_md is test
+
+import test_markdown
+import markdown_md_rendering
+
+abstract class TestMarkdownMd
+       super TestMarkdown
+
+       var md_renderer = new MarkdownRenderer
+
+       fun md_to_md(md: String): String do
+               var doc = md_parser.parse(md)
+               doc.debug
+               return md_renderer.render(doc)
+       end
+end
+
+class TestMdHeadings
+       super TestMarkdownMd
+       test
+
+       fun test_no_trailings is test do
+               var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n"""
+               var exp = """# foo\n\n## foo\n\n### foo\n\n#### foo\n\n##### foo\n\n###### foo\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test_trailings is test do
+               var md = """# foo #\n## foo ##\n### foo ###\n#### foo ####\n##### foo #####\n"""
+               var exp = """# foo #\n\n## foo ##\n\n### foo ###\n\n#### foo ####\n\n##### foo #####\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test_setext is test do
+               var md = """Foo *bar*\n=========\nFoo *bar*\n---------\n"""
+               var exp = """Foo *bar*\n=========\n\nFoo *bar*\n---------\n"""
+               assert md_to_md(md) == exp
+       end
+end
+
+class TestMdBlockQuotes
+       super TestMarkdownMd
+       test
+
+       fun test191 is test do
+               var md = """> # Foo\n> bar\n> baz\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test197 is test do
+               var md = """> foo\n---\n"""
+               var exp = """> foo\n\n---\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test198 is test do
+               var md = """> - foo\n- bar\n"""
+               var exp = """> - foo\n\n- bar\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test206 is test do
+               var md = """> foo\n> bar\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test213 is test do
+               var md = """> > > foo\n> bar\n"""
+               var exp = """> > > foo\n> > > bar\n"""
+               assert md_to_md(md) == exp
+       end
+end
+
+class TestMdLists
+       super TestMarkdownMd
+       test
+
+       fun test264 is test do
+               var md = """- foo\n- bar\n+ baz\n"""
+               var exp = """- foo\n- bar\n\n+ baz\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test265 is test do
+               var md = """1. foo\n2. bar\n3) baz\n"""
+               var exp = """1. foo\n2. bar\n\n3) baz\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test270 is test do
+               var md = """- foo\n  - bar\n    - baz\n\n      bim\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test273 is test do
+               var md = """- a\n - b\n  - c\n   - d\n  - e\n - f\n- g\n"""
+               var exp = """- a\n- b\n- c\n- d\n- e\n- f\n- g\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test274 is test do
+               var md = """1. a\n\n  2. b\n\n   3. c\n"""
+               var exp = """1. a\n\n2. b\n\n3. c\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test289 is test do
+               var md = """- a\n  - b\n  - c\n\n- d\n  - e\n  - f\n"""
+               var exp = """- a\n\n  - b\n  - c\n\n- d\n\n  - e\n  - f\n"""
+               assert md_to_md(md) == exp
+       end
+end
+
+class TestMdkListItems
+       super TestMarkdownMd
+       test
+
+       fun test217 is test do
+               var md = """1.  A paragraph\n    with two lines.\n\n        indented code\n\n    > A block quote.\n"""
+               var exp = """1. A paragraph\n   with two lines.\n\n       indented code\n\n   > A block quote.\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test219 is test do
+               var md = """- one\n\n  two\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test221 is test do
+               var md = """ -    one\n\n      two\n"""
+               var exp = """- one\n\n  two\n"""
+               assert md_to_md(md) == exp
+       end
+
+       # FIXME
+       # fun test223 is test do
+               # var md = """>>- one\n>>\n  >  > two\n"""
+               # var exp = """> > - one\n> >\n> > two\n"""
+               # assert md_to_md(md) == exp
+       # end
+
+       fun test225 is test do
+               var md = """- foo\n\n  bar\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test228 is test do
+               var md = """123456789. ok\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test230 is test do
+               var md = """0. ok\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test246 is test do
+               var md = """1. foo\n2.\n3. bar\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test254 is test do
+               var md = """  1.  A paragraph\n    with two lines.\n"""
+               var exp = """1. A paragraph\n   with two lines.\n"""
+               assert md_to_md(md) == exp
+       end
+
+       # FIXME
+       # fun test255 is test do
+               # var md = """> 1. > Blockquote\n> continued here.\n"""
+               # var exp = """> 1. > Blockquote\n     > continued here.\n"""
+               # assert md_to_md(md) == exp
+       # end
+end
+
+class TestMdFencedCodeBlocks
+       super TestMarkdownMd
+       test
+
+       fun test88 is test do
+               var md = """```\nfoo\n```\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test92 is test do
+               var md = """~~~\nfoo\n~~~\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test111 is test do
+               var md = """```ruby\ndef foo(x)\n  return 3\nend\n```\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test112 is test do
+               var md = """~~~~~~\nSome markdown:\n~~~\n**hello**\n~~~\n~~~~~~\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdIndentedCodeBlocks
+       super TestMarkdownMd
+       test
+
+       fun test75 is test do
+               var md = """    a code block\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test76 is test do
+               var md = """    a simple\n      indented code block\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test80 is test do
+               var md = """    chunk1\n\n    chunk2\n  \n \n \n    chunk3\n"""
+               assert md_to_md(md) == """    chunk1\n\n    chunk2\n\n\n\n    chunk3\n"""
+       end
+
+       fun test85 is test do
+               var md = """        foo\n    bar\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test87 is test do
+               var md = """\t\tfoo  \n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdThematicBreaks
+       super TestMarkdownMd
+       test
+
+       fun test13 is test do
+               var md = """***\n---\n___\n"""
+               var exp = """***\n\n---\n\n___\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test17 is test do
+               var md = """ ***\n  ***\n   ***\n"""
+               var exp = """***\n\n***\n\n***\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test20 is test do
+               var md = """_____________________________________\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test21 is test do
+               var md = """ - - -\n"""
+               var exp = """- - -\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test22 is test do
+               var md = """ **  * ** * ** * **\n"""
+               var exp = """**  * ** * ** * **\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test23 is test do
+               var md = """-     -      -      -\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdParagraphs
+       super TestMarkdownMd
+       test
+
+       fun test182 is test do
+               var md = """aaa\n\nbbb\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test183 is test do
+               var md = """aaa\nbbb\n\nccc\nddd\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test186 is test do
+               var md = """aaa\n             bbb\n                                       ccc\n"""
+               var exp = """aaa\nbbb\nccc\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test187 is test do
+               var md = """   aaa\nbbb\n"""
+               var exp = """aaa\nbbb\n"""
+               assert md_to_md(md) == exp
+       end
+end
+
+class TestMdHTMLBlocks
+       super TestMarkdownMd
+       test
+
+       fun test116 is test do
+               var md = """<table><tr><td>\n<pre>\n*Hello*,\n\n_world_.\n</pre>\n\n</td></tr></table>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test119 is test do
+               var md = """</div>\n*foo*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test120 is test do
+               var md = """<DIV CLASS="foo">\n\n*Markdown*\n\n</DIV>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test121 is test do
+               var md = """<div id="foo"\n  class="bar">\n</div>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test124 is test do
+               var md = """<div id="foo"\n*hi*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test137 is test do
+               var md = """<pre language="haskell"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test138 is test do
+               var md = """<script type="text/javascript">\n// JavaScript example\n\ndocument.getElementById("demo").innerHTML = "Hello JavaScript!";\n</script>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test139 is test do
+               var md = """<style\n  type="text/css">\nh1 {color:red;}\n\np {color:blue;}\n</style>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test149 is test do
+               var md = """<![CDATA[\nfunction matchwo(a,b)\n{\n  if (a < b && a < 0) then {\n    return 1;\n\n  } else {\n\n    return 0;\n  }\n}\n]]>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test150 is test do
+               var md = """  <!-- foo -->\n\n    <!-- foo -->\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+# Inlines
+
+class TestMdLinks
+       super TestMarkdownMd
+       test
+
+       fun test_autolink is test do
+               var md = """<http://foo.bar.baz/test?q=hello&id=22&boolean>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test_automail is test do
+               var md = """<MAILTO:FOO@BAR.BAZ>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test462 is test do
+               var md = """[link](/uri "title")\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test463 is test do
+               var md = """[link](/uri)\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test464 is test do
+               var md = """[link]()\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test467 is test do
+               var md = """[link](</my%20uri>)\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test483 is test do
+               var md = """[link](/url 'title "and" title')\n"""
+               assert md_to_md(md) == """[link](/url "title \\"and\\" title")\n"""
+       end
+
+       fun test490 is test do
+               var md = """[link *foo **bar** `#`*](/uri)\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdImages
+       super TestMarkdownMd
+       test
+
+       fun test546 is test do
+               var md = """![foo](/url "title")\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test552 is test do
+               var md = """![foo](train.jpg)\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test554 is test do
+               var md = """![foo](<url>)\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test555 is test do
+               var md = """![foo](train.jpg)\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdCodeSpans
+       super TestMarkdownMd
+       test
+
+       fun test316 is test do
+               var md = """`foo`\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test319 is test do
+               var md = """``foo``\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test332 is test do
+               var md = """`foo``bar``\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdEmphasisAndStrongEmphasis
+       super TestMarkdownMd
+       test
+
+       fun test333 is test do
+               var md = """*foo bar*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test335 is test do
+               var md = """a*"foo"*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test336 is test do
+               var md = """* a *\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test351 is test do
+               var md = """*(*foo*)*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test360 is test do
+               var md = """**foo bar**\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test361 is test do
+               var md = """** foo bar**\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test364 is test do
+               var md = """__foo bar__\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test393 is test do
+               var md = """*foo**bar**baz*\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdLineBreaks
+       super TestMarkdownMd
+       test
+
+       fun test609 is test do
+               var md = """foo\\\nbaz\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test612 is test do
+               var md = """foo\\\n     bar\n"""
+               var exp = """foo\\\nbar\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test613 is test do
+               var md = """*foo  \nbar*\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test619 is test do
+               var md = """foo\\\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test623 is test do
+               var md = """foo\nbaz\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdRawHTML
+       super TestMarkdownMd
+       test
+
+       fun test590 is test do
+               var md = """<a foo="bar" bam = 'baz <em>"</em>'\n_boolean zoop:33=zoop:33 />\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test591 is test do
+               var md = """Foo <responsive-image src="foo.jpg" />\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test596 is test do
+               var md = """<a href='bar'title=title>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test599 is test do
+               var md = """foo <!-- this is a\ncomment - with hyphen -->\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test601 is test do
+               var md = """foo <!--> foo -->\n\nfoo <!-- foo--->\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test602 is test do
+               var md = """foo <?php echo $a; ?>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test604 is test do
+               var md = """foo <![CDATA[>&<]]>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test606 is test do
+               var md = """foo <a href="\\*">\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdTabs
+       super TestMarkdownMd
+       test
+
+       fun test1 is test do
+               var md = """\tfoo\tbaz\t\tbim\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test2 is test do
+               var md = """  \tfoo\tbaz\t\tbim\n"""
+               var exp = """    foo\tbaz\t\tbim\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test3 is test do
+               var md = """    a\ta\n    ὐ\ta\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test4 is test do
+               var md = """  - foo\n\n\tbar\n"""
+               var exp = """- foo\n\n  bar\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test8 is test do
+               var md = """    foo\n\tbar\n"""
+               var exp = """    foo\n    bar\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test9 is test do
+               var md = """ - foo\n   - bar\n\t - baz\n"""
+               var exp = """- foo\n  - bar\n    - baz\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test10 is test do
+               var md = """#\tFoo\n"""
+               var exp = """# Foo\n"""
+               assert md_to_md(md) == exp
+       end
+
+       fun test11 is test do
+               var md = """*\t*\t*\t\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestMdBackslashEscapes
+       super TestMarkdownMd
+       test
+
+       fun test292 is test do
+               var md = """\\\t\\A\\a\\ \\3\\φ\\«\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test295 is test do
+               var md = """foo\\\nbar\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test297 is test do
+               var md = """    \\[\\]\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test298 is test do
+               var md = """~~~\n\\[\\]\n~~~\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test299 is test do
+               var md = """<http://example.com?find=\\*>\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test300 is test do
+               var md = """<a href="/bar\\/)">\n"""
+               assert md_to_md(md) == md
+       end
+end