Merge: lib/markdown2: introduce a new Markdown engine
authorJean Privat <jean@pryen.org>
Tue, 26 Jun 2018 13:28:20 +0000 (09:28 -0400)
committerJean Privat <jean@pryen.org>
Tue, 26 Jun 2018 13:28:20 +0000 (09:28 -0400)
This PR introduces a new Markdown engine called `markdown2`.

The current Markdown implementation is functional but does not follows the [CommonMark](http://commonmark.org/) specification as it passes only 273 tests of the 627 from the [CommonMark test suite](https://github.com/CommonMark/CommonMark).

Also there is a few issues with it as stated in #791, #1394, #1525 and #2507.

This new implementation aims at resolving these issues and follow the CommonMark specification.

Features:
* Markdown AST creation and rendering
* Rendering to HTML
* Rendering to Markdown
* Rendering to Man format
* Rendering to LaTeX
* Parsing of Github extended mode
* Parsing of wikilinks
* Parsing of LaTeX/Maths expression (upcoming in next PR)
* Respect of the CommonMark specification with only 10 tests failed (UTF-8 related)
* Extensive testing with a total of 980 test units...

I didn't remove the old markdown implementation since the benchmarks are not really good for now. Since `markdown2` uses a lot of regular expressions, performances can be an issue compared to `markdown`:

![screenshot from 2018-06-20 19 21 08](https://user-images.githubusercontent.com/583144/41689548-1a8bc5d0-74bf-11e8-899e-52b0eb093d57.png)

In the following PR some Nit tools will be migrated to the new implementation:
* nitunit
* nitiwiki
* nitdoc
* nitweb

Pull-Request: #2720

56 files changed:
benchmarks/markdown/bench_markdown.sh
benchmarks/markdown/engines/Makefile
benchmarks/markdown/engines/nitmd2/Makefile [new file with mode: 0644]
benchmarks/markdown/engines/nitmd2/nitmd2.nit [new file with mode: 0644]
lib/markdown2/markdown2.nit [new file with mode: 0644]
lib/markdown2/markdown_ast.nit [new file with mode: 0644]
lib/markdown2/markdown_block_parsing.nit [new file with mode: 0644]
lib/markdown2/markdown_github.nit [new file with mode: 0644]
lib/markdown2/markdown_html_rendering.nit [new file with mode: 0644]
lib/markdown2/markdown_inline_parsing.nit [new file with mode: 0644]
lib/markdown2/markdown_latex_rendering.nit [new file with mode: 0644]
lib/markdown2/markdown_man_rendering.nit [new file with mode: 0644]
lib/markdown2/markdown_md_rendering.nit [new file with mode: 0644]
lib/markdown2/markdown_rendering.nit [new file with mode: 0644]
lib/markdown2/markdown_wikilinks.nit [new file with mode: 0644]
lib/markdown2/nitmd.nit [new file with mode: 0644]
lib/markdown2/package.ini [new file with mode: 0644]
lib/markdown2/tests/commonmark_gen.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_atx_headings.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_autolinks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_backslash_escapes.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_blank_lines.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_block_quotes.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_code_spans.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_hard_line_breaks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_html_blocks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_images.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_indented_code_blocks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_inlines.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_link_reference_definitions.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_links.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_list_items.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_lists.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_paragraphs.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_precedence.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_raw_html.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_setext_headings.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_soft_line_breaks.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_tabs.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_textual_content.nit [new file with mode: 0644]
lib/markdown2/tests/test_commonmark_thematic_breaks.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_blocks.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_daring.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_github.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_headings_id.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_inlines.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_issues.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_latex.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_location.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_man.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_md.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_wikilinks.nit [new file with mode: 0644]

index d6826e1..338ba14 100755 (executable)
@@ -97,6 +97,30 @@ function bench_nitmd-o()
 }
 bench_nitmd-o
 
+function bench_nitmd2()
+{
+       name="$FUNCNAME"
+       skip_test "$name" && return
+       prepare_res $outdir/nitmd2.dat "nitmd2" "nitmd2"
+       for file in $bncdir/*.md; do
+               bench=`basename $file .md`
+               bench_command "$bench" "" "$engdir/nitmd2/nitmd2" "$file" "$s"
+       done
+}
+bench_nitmd2
+
+function bench_nitmd2-o()
+{
+       name="$FUNCNAME"
+       skip_test "$name" && return
+       prepare_res $outdir/nitmd2-o.dat "nitmd2-o" "nitmd2-o"
+       for file in $bncdir/*.md; do
+               bench=`basename $file .md`
+               bench_command "$bench" "" "$engdir/nitmd2/nitmd2-o" "$file" "$s"
+       done
+}
+bench_nitmd2-o
+
 function bench_txtmark()
 {
        name="$FUNCNAME"
index 1eeda88..711d69f 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-all: nitmd/nitmd txtmark/Txtmark.class markdown4j/Markdown4j.class
+all: nitmd/nitmd nitmd2/nitmd2 txtmark/Txtmark.class markdown4j/Markdown4j.class
 
 nitmd/nitmd:
        make -C nitmd
 
+nitmd2/nitmd2:
+       make -C nitmd2
+
 txtmark/Txtmark.class:
        make -C txtmark
 
@@ -30,6 +33,7 @@ pandoc/pandoc:
 
 clean:
        make -C nitmd clean
+       make -C nitmd2 clean
        make -C txtmark clean
        make -C markdown4j clean
        make -C pandoc clean
diff --git a/benchmarks/markdown/engines/nitmd2/Makefile b/benchmarks/markdown/engines/nitmd2/Makefile
new file mode 100644 (file)
index 0000000..526ca83
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2015 Alexandre Terrasa <alexandre@moz-code.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.
+
+NITC=../../../../bin/nitc
+
+all: nitmd2 nitmd2-o
+
+nitmd2:
+       $(NITC) nitmd2.nit
+
+nitmd2-o:
+       $(NITC) --semi-global nitmd2.nit -o $@
+
+test: all
+       ./nitmd2 ../../benches/hello.md 5
+       ./nitmd2-o ../../benches/hello.md 5
+
+clean:
+       rm -rf nitmd2 nitmd2-o
diff --git a/benchmarks/markdown/engines/nitmd2/nitmd2.nit b/benchmarks/markdown/engines/nitmd2/nitmd2.nit
new file mode 100644 (file)
index 0000000..d705618
--- /dev/null
@@ -0,0 +1,26 @@
+# 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.
+
+import markdown2
+
+var file = args.first
+var n = args[1].to_i
+
+var str = file.to_path.read_all
+var parser = new MdParser
+var renderer = new HtmlRenderer
+for i in [1..n] do
+       var doc = parser.parse(str)
+       print renderer.render(doc)
+end
diff --git a/lib/markdown2/markdown2.nit b/lib/markdown2/markdown2.nit
new file mode 100644 (file)
index 0000000..85da1b9
--- /dev/null
@@ -0,0 +1,27 @@
+# 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.
+
+module markdown2
+
+import markdown_block_parsing
+import markdown_html_rendering
+
+redef class String
+       fun md_to_html2: String do
+               var parser = new MdParser
+               var doc = parser.parse(self)
+               var renderer = new HtmlRenderer
+               return renderer.render(doc)
+       end
+end
diff --git a/lib/markdown2/markdown_ast.nit b/lib/markdown2/markdown_ast.nit
new file mode 100644 (file)
index 0000000..3de2196
--- /dev/null
@@ -0,0 +1,487 @@
+# 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 AST representation
+module markdown_ast
+
+# An abstract node
+abstract class MdNode
+
+       # Node location in original markdown
+       var location: MdLocation
+
+       # Node parent
+       var parent: nullable MdNode = null is writable
+
+       # First child
+       var first_child: nullable MdNode = null is writable
+
+       # Last child
+       var last_child: nullable MdNode = null is writable
+
+       # Previous node
+       var prev: nullable MdNode = null is writable
+
+       # Next node
+       var next: nullable MdNode = null is writable
+
+       # Children nodes of `self`
+       fun children: Array[MdNode] do
+               var nodes = new Array[MdNode]
+
+               var node = first_child
+               while node != null do
+                       nodes.add node
+                       node = node.next
+               end
+
+               return nodes
+       end
+
+       # Append a child to `self`
+       fun append_child(child: MdNode) do
+               child.unlink
+               child.parent = self
+               if last_child != null then
+                       last_child.as(not null).next = child
+                       child.prev = last_child
+                       last_child = child
+               else
+                       first_child = child
+                       last_child = child
+               end
+       end
+
+       # Prepend a child to `self`
+       fun prepend_child(child: MdNode) do
+               child.unlink
+               child.parent = self
+               if first_child != null then
+                       first_child.as(not null).prev = child
+                       child.next = first_child
+                       first_child = child
+               else
+                       first_child = child
+                       last_child = child
+               end
+       end
+
+       # Unlink `self` from its `parent`
+       fun unlink do
+               if prev != null then
+                       prev.as(not null).next = next
+               else if parent != null then
+                       parent.as(not null).first_child = next
+               end
+               if next != null then
+                       next.as(not null).prev = prev
+               else if parent != null then
+                       parent.as(not null).last_child = prev
+               end
+               parent = null
+               next = null
+               prev = null
+       end
+
+       # Insert `sibling` after `self`.
+       fun insert_after(sibling: MdNode) do
+               sibling.unlink
+               sibling.next = next
+               if sibling.next != null then
+                       sibling.next.as(not null).prev = sibling
+               end
+               sibling.prev = self
+               next = sibling
+               sibling.parent = parent
+               if sibling.next == null then
+                       sibling.parent.as(not null).last_child = sibling
+               end
+       end
+
+       # Insert `sibling` before `self`.
+       fun insert_before(sibling: MdNode) do
+               sibling.unlink
+               sibling.prev = prev
+               if sibling.prev != null then
+                       sibling.prev.as(not null).next = sibling
+               end
+               sibling.next = self
+               prev = sibling
+               sibling.parent = parent
+               if sibling.prev == null then
+                       sibling.parent.as(not null).first_child = sibling
+               end
+       end
+
+       # Visit all children or `self`
+       fun visit_all(v: MdVisitor) do
+               var node = first_child
+               while node != null do
+                       var next = node.next
+                       v.visit(node)
+                       node = next
+               end
+       end
+
+       redef fun to_s do return "{super}\{{to_s_attrs}\}"
+
+       # Returns `self` attributes as a String
+       #
+       # Mainly used for debug purposes.
+       fun to_s_attrs: String do return "loc: {location}"
+
+       # Print `self` AST
+       fun debug do
+               var v = new MdASTPrinter
+               v.enter_visit(self)
+       end
+end
+
+# A visitor for Markdown AST
+interface MdVisitor
+
+       # Start visiting `node`
+       fun enter_visit(node: MdNode) do visit(node)
+
+       # Visit `node`
+       #
+       # Method to define in specific visitor.
+       # It should not be called directly but used by `enter_visit`.
+       protected fun visit(node: MdNode) is abstract
+end
+
+# Print the AST content
+class MdASTPrinter
+       super MdVisitor
+
+       # Current indent level
+       var indent = 0
+
+       # Visit `self` to print the AST content
+       fun print_ast(node: MdNode) do
+               print "{"  " * indent}{node}"
+               indent += 1
+               node.visit_all(self)
+               indent -= 1
+       end
+
+       redef fun visit(node) do print_ast(node)
+end
+
+# Blocks
+
+# An abstract markdown block
+abstract class MdBlock
+       super MdNode
+
+       redef fun parent do return super
+
+       # Can this block contain other blocks?
+       var is_container = false
+
+       # Can this block contain `block`?
+       fun can_contain(block: MdBlock): Bool do return false
+
+       # Parents of blocks can only be blocks
+       redef fun parent=(node) do
+               assert parent == null or parent isa MdBlock else
+                       print "Parent of block must also be block."
+               end
+               super(node)
+       end
+end
+
+# A Markdown document
+class MdDocument
+       super MdBlock
+
+       redef var is_container = true
+
+       redef fun can_contain(block) do return true
+end
+
+# A block quote
+class MdBlockQuote
+       super MdBlock
+
+       redef var is_container = true
+
+       redef fun can_contain(block) do return true
+end
+
+# A block of code (indented or fenced)
+abstract class MdCodeBlock
+       super MdBlock
+
+       # Literal content
+       var literal: nullable String = null is writable
+
+       # Fence info / meta
+       var info: nullable String = null is writable
+
+       redef fun to_s_attrs do return "{super}, info={info or else "null"}, literal={literal or else "null"}"
+end
+
+# A block code that starts with an indent
+class MdIndentedCodeBlock
+       super MdCodeBlock
+
+       # Does this block use tabs instead of spaces?
+       var use_tabs: Bool
+
+       redef fun to_s_attrs do return "{super}, use_tabs={use_tabs}"
+end
+
+# A code block that starts with a fence
+class MdFencedCodeBlock
+       super MdCodeBlock
+
+       # Fence character
+       var fence_char: Char
+
+       # Fence length
+       var fence_length: Int
+
+       # Fence indentation level
+       var fence_indent: Int
+
+       redef fun to_s_attrs do return "{super}, fence_char={fence_char}, fence_length={fence_length}, fence_indent={fence_indent}"
+end
+
+# A heading
+class MdHeading
+       super MdBlock
+
+       # Heading level (from 1 to 6)
+       var level: Int
+
+       # Is this heading in the setext format in the original source?
+       var is_setext = false is optional
+
+       # Does this heading has an atx trailing in the original source?
+       var has_atx_trailing = false is optional
+
+       redef fun to_s_attrs do return "{super}, level={level}"
+end
+
+# An html block
+class MdHtmlBlock
+       super MdBlock
+
+       # Literal content
+       var literal: nullable String = null is writable
+
+       redef fun to_s_attrs do return "{super}, literal={literal or else "null"}"
+end
+
+# An ordered or unordered list block
+abstract class MdListBlock
+       super MdBlock
+
+       # Does this list contains line breaks?
+       var is_tight: Bool = false is writable
+
+       redef var is_container = true
+
+       redef fun can_contain(block) do return block isa MdListItem
+
+       redef fun to_s_attrs do return "{super}, is_tight={is_tight}"
+end
+
+# An ordered or unordered list item block
+class MdListItem
+       super MdBlock
+
+       redef var is_container = true
+
+       redef fun can_contain(block) do return true
+end
+
+# An ordered list block
+class MdOrderedList
+       super MdListBlock
+
+       # List starting number
+       var start_number: Int
+
+       # List number delimiter
+       var delimiter: Char
+
+       redef fun to_s_attrs do return "{super}, start_number={start_number}, delimiter={delimiter}"
+end
+
+# An unordered list
+class MdUnorderedList
+       super MdListBlock
+
+       # List bullet marker
+       var bullet_marker: Char
+
+       redef fun to_s_attrs do return "{super}, bullet_marker={bullet_marker}"
+end
+
+# A paragraph block
+class MdParagraph
+       super MdBlock
+
+       # Is this paragraph in a list?
+       fun is_in_list: Bool do
+               var parent = self.parent
+               return parent != null and parent.parent isa MdListBlock
+       end
+
+       # Is this paragraph in a tight list?
+       fun is_in_tight_list: Bool do
+               var parent = self.parent
+               if parent == null then return false
+               var gramps = parent.parent
+               return gramps isa MdListBlock and gramps.is_tight
+       end
+end
+
+# A ruler
+class MdThematicBreak
+       super MdBlock
+
+       # Thematic break pattern used in markdown source
+       var original_pattern: String
+end
+
+# Inline nodes
+
+# A line break (soft or hard)
+abstract class MdLineBreak
+       super MdNode
+end
+
+# A hardline break (`\\n` or `  \n`)
+class MdHardLineBreak
+       super MdLineBreak
+
+       # Does this line break used a backslash in the original source?
+       var has_backslash: Bool
+end
+
+# A soft line breack (`\r` or `\n`)
+class MdSoftLineBreak
+       super MdLineBreak
+end
+
+# An inline code string
+class MdCode
+       super MdNode
+
+       # Emphasis delimiter
+       var delimiter: String
+
+       # Literal content
+       var literal: String is writable
+
+       redef fun to_s_attrs do return "{super}, literal={literal}"
+end
+
+# A node that users delimiters in the Markdown form
+#
+# For example the emphasis: `*bold*`.
+abstract class MdDelimited
+       super MdNode
+
+       # Emphasis delimiter
+       var delimiter: String
+
+       # Opening delimiter
+       fun opening_delimiter: String do return delimiter
+
+       # Closing delimiter
+       fun closing_delimiter: String do return delimiter
+
+       redef fun to_s_attrs do return "{super}, delimiter={delimiter}"
+end
+
+# An emphasis
+class MdEmphasis
+       super MdDelimited
+end
+
+# A strong emphasis token
+class MdStrongEmphasis
+       super MdDelimited
+end
+
+# An inlined html string
+class MdHtmlInline
+       super MdNode
+
+       # Literal content
+       var literal: String is writable
+
+       redef fun to_s_attrs do return "{super}, literal={literal}"
+end
+
+# A link or image
+abstract class MdLinkOrImage
+       super MdNode
+
+       # Link destination
+       var destination: String is writable
+
+       # Link title
+       var title: nullable String is writable
+
+       # Is the `destination` between pointy brackets `<dest>`
+       var has_brackets = false is writable
+
+       redef fun to_s_attrs do return "{super}, destination={destination}, title={title or else "null"}"
+end
+
+# An image
+class MdImage
+       super MdLinkOrImage
+end
+
+# A link
+class MdLink
+       super MdLinkOrImage
+
+       # Is this link an autolink?
+       var is_autolink = false is optional, writable
+end
+
+# A raw text token
+class MdText
+       super MdNode
+
+       # Literal content
+       var literal: String is writable
+
+       redef fun to_s_attrs do return "{super}, literal={literal}"
+end
+
+# MdNode location in the Markdown input
+class MdLocation
+
+       # Starting line number (starting from 1).
+       var line_start: Int is writable
+
+       # Starting column number (starting from 1).
+       var column_start: Int is writable
+
+       # Stopping line number (starting from 1).
+       var line_end: Int is writable
+
+       # Stopping column number (starting from 1).
+       var column_end: Int is writable
+
+       redef fun to_s do return "{line_start},{column_start}--{line_end},{column_end}"
+end
diff --git a/lib/markdown2/markdown_block_parsing.nit b/lib/markdown2/markdown_block_parsing.nit
new file mode 100644 (file)
index 0000000..cfa07b5
--- /dev/null
@@ -0,0 +1,1503 @@
+# 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 blocks parsing
+#
+# Introduce the parsers for the different Markdown blocks such as headings, lists
+# code blocks etc.
+module markdown_block_parsing
+
+import markdown_inline_parsing
+
+# Markdown parser
+#
+# Used to create the AST representation of a Markdown document.
+class MdParser
+
+       # Inline parser used to parse block content
+       private var inline_parser = new MdInlineParser is lazy
+
+       # Block parsers factories
+       private var block_parser_factories: Collection[MdBlockParserFactory] do
+               var factories = new Array[MdBlockParserFactory]
+               factories.add new MdBlockQuoteParserFactory
+               factories.add new MdHeadingParserFactory
+               factories.add new MdFencedCodeBlockParserFactory
+               factories.add new MdHtmlBlockParserFactory
+               factories.add new MdThematicBreakParserFactory
+               factories.add new MdListBlockParserFactory
+               factories.add new MdIndentedCodeBlockParserFactory
+               return factories
+       end
+
+       # Active block parsers
+       #
+       # Used as a stack to parse nested blocks.
+       private var active_block_parsers = new Array[MdBlockParser]
+
+       # All active block parsers
+       private var all_block_parsers = new HashSet[MdBlockParser]
+
+       # Return the active block parser
+       #
+       # The last entry in the `active_block_parsers` stack.
+       private fun active_block_parser: MdBlockParser do
+               return active_block_parsers.last
+       end
+
+       # Activate a `block_parser`
+       #
+       # Add the `block_parser` on the top of the `active_block_parsers` stack.
+       # Also register it in `all_block_parsers`.
+       private fun activate_block_parser(block_parser: MdBlockParser) do
+               active_block_parsers.add block_parser
+               all_block_parsers.add block_parser
+       end
+
+       # Deactivate the `active_block_parser`
+       private fun deactivate_block_parser do
+               active_block_parsers.pop
+       end
+
+       # Deactivate and remove the `active_block_parser` from the `all_block_parsers` list
+       private fun remove_active_block_parser do
+               var old = active_block_parser
+               deactivate_block_parser
+               all_block_parsers.remove(old)
+               old.block.unlink
+       end
+
+       # Post-processors applied after the parsing of a document
+       var post_processors = new Array[MdPostProcessor] is writable
+
+       # Currently parsed line
+       private var line_string: String is noinit
+
+       # Current index (offset) in input `line_string` (starts at 0)
+       private var index = 0
+
+       # Current column in input `line_string` (starts at 0)
+       #
+       # Tab causes column to go to next 4-space tab stop.
+       private var column = 0
+
+       # Is the current column within a tab character (partially consumed tab)
+       private var column_is_in_tab: Bool is noinit
+
+       # Current line in input string (starts at 1)
+       private var line = 1
+
+       # Index of the next non-space character starting from `index`
+       private var next_non_space_index = 0
+
+       # Next non-space column
+       private var next_non_space_column = 0
+
+       # Current indent in columns
+       #
+       # Either by spaces or tab stop of 4, starting from `column`.
+       private var indent = 0
+
+       # Is the current `line` blank starting from `index`?
+       private var is_blank: Bool is noinit
+
+       # Does a node end with a blank line?
+       private var last_line_blank = new HashMap[MdNode, Bool]
+
+       # Initialize parser state
+       private fun initialize do
+               active_block_parsers.clear
+               all_block_parsers.clear
+               index = 0
+               column = 0
+               column_is_in_tab = false
+               line = 1
+               next_non_space_index = 0
+               next_non_space_column = 0
+               indent = 0
+               is_blank = false
+               last_line_blank.clear
+       end
+
+       # Parse the `input` string as a MdDocument
+       fun parse(input: String): MdDocument do
+               initialize
+
+               var document_block_parser = new MdDocumentBlockParser(1, 1, 0)
+               activate_block_parser(document_block_parser)
+               var line_start = 0
+               var line_break = find_line_break(input, line_start)
+               while line_break != -1 do
+                       var line_string = input.substring(line_start, line_break - line_start)
+                       incorporate_line(line_string)
+                       if line_break + 1 < input.length and
+                          input.chars[line_break] == '\r' and
+                          input.chars[line_break + 1] == '\n' then
+                               line_start = line_break + 2
+                       else
+                               line_start = line_break + 1
+                       end
+                       line_break = find_line_break(input, line_start)
+                       line += 1
+                       column = 0
+               end
+
+               # Finalize pending line
+               if input.length > 0 and (line_start == 0 or line_start < input.length) then
+                       incorporate_line(input.substring(line_start, input.length - line_start))
+               end
+               finalize_blocks(active_block_parsers)
+
+               # Walk through a block and its chiildren revursively
+               # Parsing string content into inline content where appropriate.
+               var all_block_parsers = all_block_parsers.to_a
+               var i = all_block_parsers.length - 1
+               while i >= 0 do
+                       var block_parser = all_block_parsers[i]
+                       block_parser.parse_inlines(inline_parser)
+                       i -= 1
+               end
+               var document = document_block_parser.block
+               return document
+       end
+
+       # Post-process the `document`
+       fun post_process(document: MdDocument) do
+               for processor in post_processors do
+                       processor.post_process(self, document)
+               end
+       end
+
+       # Analyze a line of text and update the document
+       #
+       # We parse Markdown text by calling this on each line of `input`.
+       private fun incorporate_line(input: String) do
+               line_string = input
+               index = 0
+               column = 0
+               column_is_in_tab = false
+
+               # For each containing block, try to parse the associated line start.
+               var matches = 1
+               for i in [1 .. active_block_parsers.length[ do
+                       var block_parser = active_block_parsers[i]
+                       find_next_non_space
+
+                       var result = block_parser.try_continue(self)
+                       if result isa MdBlockContinue then
+                               if result.is_finalize then
+                                       block_parser.finalize(self)
+                                       return
+                               else
+                                       if result.new_index != -1 then
+                                               set_new_index result.new_index
+                                       else if result.new_column != -1 then
+                                               set_new_column result.new_column
+                                       end
+                               end
+                               matches += 1
+                       else
+                               break
+                       end
+               end
+
+               var unmatched_block_parsers = active_block_parsers.subarray(
+                       matches, active_block_parsers.length - matches)
+               var last_matched_block_parser = active_block_parsers[matches - 1]
+               var block_parser = last_matched_block_parser
+               var all_closed = unmatched_block_parsers.is_empty
+
+               # Unless last matched container is a code block, try new container starts,
+               # adding children to the last matched container.
+               var try_block_starts = block_parser.block isa MdParagraph or
+                       block_parser.block.is_container
+
+               while try_block_starts do
+                       find_next_non_space
+
+                       # Optimize lookup
+                       if is_blank or (indent < 4 and line_string.chars[next_non_space_index].is_letter) then
+                               set_new_index next_non_space_index
+                               break
+                       end
+
+                       var block_start = find_block_start(block_parser)
+                       if block_start == null then
+                               set_new_index next_non_space_index
+                               break
+                       end
+
+                       if not all_closed then
+                               finalize_blocks(unmatched_block_parsers)
+                               all_closed = true
+                       end
+
+                       if block_start.new_index != -1 then
+                               set_new_index block_start.new_index
+                       else if block_start.new_column != -1 then
+                               set_new_column block_start.new_column
+                       end
+
+                       if block_start.replace_active_block_parser then
+                               remove_active_block_parser
+                       end
+
+                       for new_block_parser in block_start.block_parsers do
+                               add_child(new_block_parser)
+                               block_parser = new_block_parser
+                               try_block_starts = new_block_parser.block.is_container
+                       end
+               end
+
+               # What remains at the offset is a text line.
+               # Add the text to the appropriate block.
+
+               # First check for a lazy paragraph continuation
+               if not all_closed and not is_blank and active_block_parser isa MdParagraphParser then
+                       add_line
+               else
+                       # Finalize any blocks not matched
+                       if not all_closed then
+                               finalize_blocks(unmatched_block_parsers)
+                       end
+                       propagate_last_line_blank(block_parser, last_matched_block_parser)
+
+                       if not block_parser.block.is_container then
+                               add_line
+                       else if not is_blank then
+                               # Create a paragraph container for the line
+                               add_child(new MdParagraphParser(line, column + 1, block_parser.content_offset))
+                               add_line
+                       end
+               end
+       end
+
+       # Find what kind of block starts at `index` in `input`
+       private fun find_block_start(block_parser: MdBlockParser): nullable MdBlockStart do
+               for block_parser_factory in block_parser_factories do
+                       var result = block_parser_factory.try_start(self, block_parser)
+                       if result != null then return result
+               end
+               return null
+       end
+
+       # Add a `block_parser` block's as child of the active block parser block
+       private fun add_child(block_parser: MdBlockParser) do
+               # Finalize non-parentable blocks
+               while not active_block_parser.block.can_contain(block_parser.block) do
+                       active_block_parser.finalize(self)
+               end
+               # Append block block parser block to its parent
+               active_block_parser.block.append_child(block_parser.block)
+               activate_block_parser(block_parser)
+       end
+
+       # Add line content to the active block parser
+       #
+       # We assume it can accept lines.
+       private fun add_line do
+               var content = null
+               if column_is_in_tab then
+                       # Out column is in a partially consumed tab.
+                       # Expand the remaining columns to the next tab stop to spaces.
+                       var after_tab = index + 1
+                       var rest = line_string.substring(after_tab, line_string.length - after_tab)
+                       var spaces = column.columns_to_next_tab_stop
+                       var buffer = new Buffer
+                       for i in [0 .. spaces[ do
+                               buffer.add ' '
+                       end
+                       buffer.append(rest)
+                       content = buffer.write_to_string
+               else
+                       content = line_string.substring(index, line_string.length - index)
+               end
+               active_block_parser.add_line(content)
+       end
+
+       # Finalize blocks of previous line
+       private fun finalize_blocks(block_parsers: Sequence[MdBlockParser]) do
+               var i = block_parsers.length - 1
+               while i >= 0 do
+                       var block_parser = block_parsers[i]
+                       block_parser.finalize(self)
+                       i -= 1
+               end
+       end
+
+       # Advance the `index` position to the next character
+       #
+       # Also set the `column`.
+       # If the next character is a tab, compute the new column accordingly.
+       private fun advance do
+               var c = line_string.chars[index]
+               if c == '\t' then
+                       index += 1
+                       column += column.columns_to_next_tab_stop
+               else
+                       index += 1
+                       column += 1
+               end
+       end
+
+       # Move `index` to the next non-space character index in the `input` string
+       #
+       # Also set `next_non_space_index`, `next_non_space_column`, `is_blank` and `indent`.
+       private fun find_next_non_space do
+               var i = index
+               var cols = column
+
+               is_blank = true
+               while i < line_string.length do
+                       var c = line_string.chars[i]
+                       if c == ' ' then
+                               i += 1
+                               cols += 1
+                               continue
+                       else if c == '\t' then
+                               i += 1
+                               cols += 4 - (cols % 4)
+                               continue
+                       end
+                       is_blank = false
+                       break
+               end
+
+               next_non_space_index = i
+               next_non_space_column = cols
+               indent = next_non_space_column - column
+       end
+
+       # Return the position of the next line break
+       #
+       # We consider `\r` and `\n`.
+       private fun find_line_break(input: String, start_index: Int): Int do
+               for i in [start_index .. input.length[ do
+                       var char = input.chars[i]
+                       if char == '\r' or char == '\n' then return i
+               end
+               return -1
+       end
+
+       # Set the parser `index` at `new_index`
+       #
+       # Also set `column` and `column_is_in_tab`.
+       private fun set_new_index(new_index: Int) do
+               if new_index >= next_non_space_index then
+                       # We can start from here, no need to calculate tab stops again
+                       index = next_non_space_index
+                       column = next_non_space_column
+               end
+               while index < new_index and index != line_string.length do
+                       advance
+               end
+               # If we're going to an index as opposed to a column, we're never within a tab
+               column_is_in_tab = false
+       end
+
+       # Set the parser `column` at `new_column`
+       #
+       # Also set `index` and `column_is_in_tab`.
+       private fun set_new_column(new_column: Int) do
+               if new_column >= next_non_space_column then
+                       # We can start from here, no need to calculate tab stops again
+                       index = next_non_space_index
+                       column = next_non_space_column
+               end
+               while column < new_column and index != line_string.length do
+                       advance
+               end
+               if column > new_column then
+                       # Last character was a tab and we overshot our target
+                       index -= 1
+                       column = new_column
+                       column_is_in_tab = true
+               else
+                       column_is_in_tab = false
+               end
+       end
+
+       # Does `block` end with a blank line?
+       private fun ends_with_blank_line(block: nullable MdNode): Bool do
+               while block != null do
+                       if is_last_line_blank(block) then return true
+                       if block isa MdListBlock or block isa MdListItem then
+                               block = block.last_child
+                       else
+                               break
+                       end
+               end
+               return false
+       end
+
+       # Propagate a blank line to all block_parser blocl's parents
+       private fun propagate_last_line_blank(block_parser: MdBlockParser, last_matched_block_parser: MdBlockParser) do
+               var last_child = block_parser.block.last_child
+               if is_blank and last_child != null then
+                       last_line_blank[last_child] = true
+               end
+               var block = block_parser.block
+
+               # Block quotes lines are never blank as they start with `>`.
+               # We don't count blanks in fenced code for purposes of thight/loose lists.
+               # We also don't set `last_line_blank` on an empty list item.
+               var last_line_blank = is_blank and
+                       not (block isa MdBlockQuote or
+                            block isa MdFencedCodeBlock or
+                                (block isa MdListItem and block.first_child == null and
+                                                                                 block_parser != last_matched_block_parser))
+
+               # Propagate `last_line_blank` up through parents
+               var node: nullable MdNode = block_parser.block
+               while node != null do
+                       self.last_line_blank[node] = last_line_blank
+                       node = node.parent
+               end
+       end
+
+       # Is last line blank for `node`?
+       private fun is_last_line_blank(node: MdNode): Bool do
+               if not last_line_blank.has_key(node) then return false
+               return last_line_blank[node]
+       end
+end
+
+# Block parsing
+
+# Parser for a specific block node
+abstract class MdBlockParser
+
+       # Kind of block under construction
+       type BLOCK: MdBlock
+
+       # MdBlock under construction
+       fun block: BLOCK is abstract
+
+       # Line Start
+       var line_start: Int
+
+       # Column start
+       var column_start: Int
+
+       # Location at start
+       #
+       # The location end it initialized at `-1` and will be set later in the
+       # `finalize` method.
+       var location: MdLocation is lazy do return new MdLocation(line_start, column_start, -1, -1)
+
+       # Column where the content starts
+       var content_offset: Int
+
+       # Initialize the current `block`
+       fun initialize(parser: MdParser) do end
+
+       # Can `self` continue from the current `index` in `parser`?
+       #
+       # Return a new `MdBlockContinue` if `self` can continue parsing.
+       # Return null otherwise.
+       fun try_continue(state: MdParser): nullable MdBlockContinue is abstract
+
+       # Add `line` to the current `block`
+       fun add_line(line: String) do end
+
+       # Finalize the current `block`
+       #
+       # Deactivate `self` from `parser` and call `close_block`.
+       fun finalize(parser: MdParser) do
+               if parser.active_block_parser == self then
+                       parser.deactivate_block_parser
+               end
+       end
+
+       # Parse `block` lines
+       fun parse_inlines(inline_parser: MdInlineParser) do end
+end
+
+# Result object for continuing parsing of a block
+class MdBlockContinue
+
+       # Index from which continue parsing
+       var new_index: Int
+
+       # Column from which continue parsing
+       var new_column: Int
+
+       # Is the block finalized?
+       var is_finalize: Bool
+
+       # Continue from index
+       init at_index(new_index: Int) do
+               init(new_index, -1, false)
+       end
+
+       # Continue from column
+       init at_column(new_column: Int) do
+               init(-1, new_column, false)
+       end
+
+       # Block is finished
+       init finished do
+               init(-1, -1, true)
+       end
+end
+
+# Block parser factory for a block node for determining when a block starts
+abstract class MdBlockParserFactory
+
+       # Can the associated block parser can start at the current line in `parser`?
+       #
+       # Return a new `MdBlockStart` if the block parser can start.
+       # Return null otherwise.
+       fun try_start(parser: MdParser, matched_block_parser: MdBlockParser):
+               nullable MdBlockStart is abstract
+end
+
+# Result object from starting parsing of a block
+class MdBlockStart
+
+       # Block parsers for this block start
+       var block_parsers: Array[MdBlockParser]
+
+       # Index where the parsing should start
+       var new_index = -1
+
+       # Column where the parsing should start
+       var new_column = -1
+
+       # Does the block starting with `self` terminate a previous block?
+       var replace_active_block_parser = false
+
+       # Start from `new_index`
+       fun at_index(new_index: Int): MdBlockStart do
+               self.new_index = new_index
+               return self
+       end
+
+       # Start from `new_column`
+       fun at_column(new_column: Int): MdBlockStart do
+               self.new_column = new_column
+               return self
+       end
+
+       # Start replacing the active block parser
+       fun replacing_active_block_parser: MdBlockStart do
+               self.replace_active_block_parser = true
+               return self
+       end
+end
+
+# Parser for the whole document
+class MdDocumentBlockParser
+       super MdBlockParser
+
+       redef type BLOCK: MdDocument
+       redef var block = new MdDocument(location) is lazy
+
+       # Always continue at current indent
+       redef fun try_continue(state) do return new MdBlockContinue.at_index(state.index)
+
+       redef fun finalize(parser) do
+       end
+
+       # redef fun finalize(state) do
+       redef fun parse_inlines(inline_parser) do
+               var last_child = block.last_child
+               if last_child != null then
+                       location.line_end = last_child.location.line_end
+                       location.column_end = last_child.location.column_end
+               end
+       end
+end
+
+# Headings parser
+class MdHeadingParser
+       super MdBlockParser
+
+       redef type BLOCK: MdHeading
+
+       redef var block = new MdHeading(location, level, is_setext, has_atx_trailing) is lazy
+
+       redef var location = new MdLocation(line_start, column_start, line_end, column_end) is lazy
+
+       # Line end
+       var line_end: Int
+
+       # Column end
+       var column_end: Int
+
+       # Heading level
+       var level: Int
+
+       # Heading content
+       var content: String
+
+       # Heading has ATX trailing
+       var has_atx_trailing: Bool
+
+       # Heading is setext format
+       var is_setext: Bool
+
+       # Never continue parsing as an heading is a one liner
+       redef fun try_continue(state) do return null
+
+       # Parse the heading content
+       redef fun parse_inlines(inline_parser) do
+               inline_parser.parse(content, content_offset, block)
+       end
+end
+
+# Heading parser factory
+class MdHeadingParserFactory
+       super MdBlockParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               if state.indent >= 4 then return null
+
+               var next_non_space = state.next_non_space_index
+               var line = state.line_string
+               var paragraph = null
+               if matched_block_parser isa MdParagraphParser then
+                       paragraph = matched_block_parser.content
+               end
+
+               var line_content = line.substring(next_non_space, line.length - next_non_space)
+               var match = line_content.search(re_atx_heading)
+               if match != null then
+                       # ATX heading
+                       var new_offset = next_non_space + match.subs.first.as(not null).length
+                       var level = match.subs.first.as(not null).to_s.trim.length
+                       # remove trailing ###s
+                       var after_leading = line.substring(new_offset, line.length - new_offset)
+                       var trailing = after_leading.search(re_atx_trailing)
+                       var has_trailing = trailing != null
+                       var trailing_length = if trailing != null then trailing.length else 0
+                       var content = after_leading.replace(re_atx_trailing, "")
+                       return (new MdBlockStart(
+                               [new MdHeadingParser(
+                                       state.line,
+                                       next_non_space + 1,
+                                       new_offset + 1,
+                                       state.line,
+                                       new_offset + content.length + trailing_length,
+                                       level,
+                                       content,
+                                       has_trailing, false)])
+                               ).at_index(line.length)
+               end
+
+               if paragraph ==  null then return null
+
+               match = line_content.search(re_setext_heading)
+               if match == null then return null
+               var level = 2
+               if match.subs.first.as(not null).to_s.chars.first == '=' then level = 1
+               var content = paragraph.to_s
+               return (new MdBlockStart(
+                       [new MdHeadingParser(
+                               state.line - 1,
+                               next_non_space + 1,
+                               0,
+                               state.line,
+                               state.column + match.length,
+                               level,
+                               content,
+                               false, true)])
+                       ).at_index(line.length).replacing_active_block_parser
+       end
+end
+
+# Blockquotes parser
+class MdBlockQuoteParser
+       super MdBlockParser
+
+       redef type BLOCK: MdBlockQuote
+       redef var block = new MdBlockQuote(location) is lazy
+
+       redef fun try_continue(state) do
+               var next_non_space = state.next_non_space_index
+               var indent = state.indent
+               var line = state.line_string
+
+               if indent >= 4 then return null
+               if next_non_space >= line.length then return null
+               if line.chars[next_non_space] != '>' then return null
+
+               var new_column = state.column + state.indent + 1
+               # optional following space or tab
+               if state.line_string.is_space_or_tab(next_non_space + 1) then
+                       new_column += 1
+               end
+               return new MdBlockContinue.at_column(new_column)
+       end
+
+       redef fun parse_inlines(inline_parser) do
+               var last_child = block.last_child
+               if last_child != null then
+                       location.line_end = last_child.location.line_end
+                       location.column_end = last_child.location.column_end
+               end
+       end
+end
+
+# Blockquotes parser factory
+class MdBlockQuoteParserFactory
+       super MdBlockParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               var next_non_space = state.next_non_space_index
+               var indent = state.indent
+               var line = state.line_string
+
+               if indent >= 4 then return null
+               if next_non_space >= line.length then return null
+               if line.chars[next_non_space] != '>' then return null
+
+               var new_column = state.column + state.indent + 1
+               # optional following space or tab
+               if state.line_string.is_space_or_tab(next_non_space + 1) then
+                       new_column += 1
+               end
+               return (new MdBlockStart(
+                       [new MdBlockQuoteParser(
+                               state.line,
+                               state.column + 1,
+                               new_column)])
+                       ).at_column(new_column)
+       end
+end
+
+# Indented code blocks parser
+class MdIndentedCodeBlockParser
+       super MdBlockParser
+
+       redef type BLOCK: MdIndentedCodeBlock
+       redef var block = new MdIndentedCodeBlock(location, use_tabs) is lazy
+
+       # Indent is tab?
+       var use_tabs: Bool
+
+       # Block content
+       var content = new Buffer
+
+       redef fun try_continue(state) do
+               if state.indent >= 4 then
+                       return new MdBlockContinue.at_column(state.column + 4)
+               else if state.is_blank then
+                       return new MdBlockContinue.at_index(state.next_non_space_index)
+               end
+               return null
+       end
+
+       redef fun add_line(line) do
+               if not content.is_empty then
+                       content.add('\n')
+               end
+               content.append(line)
+       end
+
+       redef fun finalize(parser) do
+               super
+
+               add_line(" ")
+               var content = self.content.to_s
+               var literal = content.replace_first(re_trailing_blank_lines, "\n")
+               block.literal = literal
+
+               var lines = literal.split("\n")
+               location.line_end = location.line_start + lines.length - 2
+               location.column_end = content_offset + lines[lines.length - 2].length + 4
+       end
+end
+
+# Indented code blocks parser factory
+class MdIndentedCodeBlockParserFactory
+       super MdBlockParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               if state.indent < 4 then return null
+               if state.is_blank then return null
+               if state.active_block_parser.block isa MdParagraph then return null
+
+               var use_tabs = state.line_string.has_prefix("\t")
+               return (new MdBlockStart(
+                       [new MdIndentedCodeBlockParser(
+                               state.line,
+                               state.column + 1,
+                               state.column,
+                               use_tabs)])
+                       ).at_column(state.column + 4)
+       end
+end
+
+# Fenced code blocks parser
+class MdFencedCodeBlockParser
+       super MdBlockParser
+
+       redef type BLOCK: MdFencedCodeBlock
+       redef var block = new MdFencedCodeBlock(location, fence_char, fence_length, fence_indent) is lazy
+
+       # Fence character
+       var fence_char: Char
+
+       # Fence length
+       var fence_length: Int
+
+       # Fence indent
+       var fence_indent: Int
+
+       # Fence first line
+       var first_line: nullable String = null
+
+       # Fence other lines
+       var other_lines = new Buffer
+
+       redef fun try_continue(state) do
+               var next_non_space = state.next_non_space_index
+               var new_index = state.index
+               var line = state.line_string
+
+               if state.indent <= 3 and next_non_space < line.length and
+                  line.chars[next_non_space] == fence_char then
+
+                       var match = line.substring(next_non_space, line.length - next_non_space).
+                               search(re_closing_fence)
+                       if match != null and match.subs[0].as(not null).length >= fence_length then
+                               # closing fence - we're at end of line, so we can finalize now
+                               return new MdBlockContinue.finished
+                       end
+               end
+
+               # skip optional spaces of fence indent
+               var i = fence_indent
+               while i > 0 and new_index < line.length and line.chars[new_index] == ' ' do
+                       new_index += 1
+                       i -= 1
+               end
+
+               return new MdBlockContinue.at_index(new_index)
+       end
+
+       redef fun add_line(line) do
+               if first_line == null then
+                       first_line = line
+               else
+                       other_lines.append(line)
+                       other_lines.add '\n'
+               end
+       end
+
+       redef fun finalize(parser) do
+               super
+
+               # first line become info string
+               var first_line = self.first_line
+               if first_line != null then
+                       var info = first_line.trim.unescape_string
+                       if not info.is_empty then block.info = info
+               end
+
+               var content = other_lines.to_s
+               block.literal =  content
+
+               var lines = content.split("\n")
+               location.line_end = location.line_start + lines.length
+               location.column_end = content_offset + fence_indent + fence_length
+       end
+end
+
+# Fenced code blocks parser factory
+class MdFencedCodeBlockParserFactory
+       super MdBlockQuoteParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               var next_non_space = state.next_non_space_index
+               var line = state.line_string
+
+               if state.indent >= 4 then return null
+
+               var match = line.substring(next_non_space, line.length - next_non_space).search(re_opening_fence)
+               if match == null then return null
+
+               var fence_length
+               var fence_char
+               var sub0 = match.subs[0]
+               if sub0 != null then
+                       fence_length = sub0.length
+                       fence_char = sub0.to_s.chars.first
+               else
+                       fence_length = match.subs[2].as(not null).length
+                       fence_char = match.subs[2].as(not null).to_s.chars.first
+               end
+               if fence_char == '`' and match.to_s.has("[^`]+`".to_re) then
+                       return null
+               else if match.to_s.has("[^~]+~".to_re) then
+                       return null
+               end
+               return (new MdBlockStart(
+                       [new MdFencedCodeBlockParser(
+                               state.line,
+                               state.column + 1,
+                               state.column,
+                               fence_char,
+                               fence_length,
+                               state.indent)]
+                       )).at_index(next_non_space + fence_length)
+       end
+end
+
+# List blocks parser
+class MdListBlockParser
+       super MdBlockParser
+
+       redef type BLOCK: MdListBlock
+
+       redef var block is lazy do
+               if is_ordered then
+                       return new MdOrderedList(location, digit.as(not null), delim.as(not null))
+               else
+                       return new MdUnorderedList(location, bullet.as(not null))
+               end
+       end
+
+       # Is this list ordered
+       var is_ordered: Bool
+
+       # List bullet if unordered
+       var bullet: nullable Char
+
+       # List digit if ordered
+       var digit: nullable Int
+
+       # List delimiter if ordered
+       var delim: nullable Char
+
+       redef fun try_continue(state) do return new MdBlockContinue.at_index(state.index)
+
+       redef fun finalize(parser) do
+               super
+
+               var item = block.first_child
+               while item != null do
+                       # check for non-final list item ending with blank line
+                       if parser.ends_with_blank_line(item) and item.next != null then
+                               block.is_tight = false
+                               break
+                       end
+                       # recurse into children of list item to see if there are spaces between any of them
+                       var sub_item = item.first_child
+                       while sub_item != null do
+                               if parser.ends_with_blank_line(sub_item) and
+                                  (item.next != null or sub_item.next != null) then
+                                       block.is_tight = false
+                                       break
+                               end
+                               sub_item = sub_item.next
+                       end
+                       item = item.next
+               end
+       end
+
+       redef fun parse_inlines(inline_parser) do
+               var last_child = block.last_child
+               if last_child != null then
+                       location.line_end = last_child.location.line_end
+                       location.column_end = last_child.location.column_end
+               end
+       end
+end
+
+# List blocks parser factory
+class MdListBlockParserFactory
+       super MdBlockQuoteParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               if state.indent >= 4 and not matched_block_parser isa MdListBlockParser then return null
+
+               var marker_index = state.next_non_space_index
+               var marker_column = state.column + state.indent
+
+               var in_paragraph = matched_block_parser isa MdParagraphParser and matched_block_parser.content != null
+               var list_data = parse_list_marker(state, state.line_string, marker_index, marker_column, in_paragraph)
+               if list_data == null then return null
+
+
+               var new_column = list_data.content_column
+               var list_item_parser = new MdListItemParser(
+                       state.line,
+                       state.column + 1,
+                       new_column,
+                       new_column - state.column)
+
+               # prepend the list block if needed
+               if not matched_block_parser isa MdListBlockParser or not lists_match(matched_block_parser.block, list_data) then
+                       var list_block_parser = new MdListBlockParser(state.line, state.column + 1, new_column - state.column, list_data.is_ordered, list_data.bullet, list_data.digit, list_data.delim)
+                       list_block_parser.block.is_tight = true
+
+                       return (new MdBlockStart([list_block_parser, list_item_parser: MdBlockParser])).at_column(new_column)
+               end
+               return (new MdBlockStart([list_item_parser])).at_column(new_column)
+       end
+
+       private fun parse_list_marker(state: MdParser, line: String, marker_index, marker_column: Int, in_paragraph: Bool): nullable MdListData do
+               var rest = line.substring(marker_index, line.length - marker_index)
+               var match = rest.search(re_list_marker)
+               if match == null then return null
+
+               var is_ordered
+               var bullet = null
+               var digit = null
+               var delim = null
+
+               var bullet_match = match.subs[0]
+               if bullet_match != null then
+                       is_ordered = false
+                       bullet = bullet_match.to_s.chars[0]
+               else
+                       is_ordered = true
+                       digit = match.subs[2].as(not null).to_s.to_i
+                       delim = match.subs[3].as(not null).to_s.chars[0]
+               end
+
+               var marker_length = match.length
+               if match.to_s.has_suffix(" ") or match.to_s.has_suffix("\t") then
+                       marker_length -= 1
+               end
+               var index_after_marker = marker_index + marker_length
+
+               # marker doesn't include tabs, so counting them as column directly is ok
+               var column_after_marker = marker_column + marker_length
+               # the column within the line where the content starts
+               var content_column = column_after_marker
+
+               # see at which column the content starts if there is content
+               var has_content = false
+               for i in [index_after_marker .. line.length[ do
+                       var c = line.chars[i]
+                       if c == '\t' then
+                               content_column += content_column.columns_to_next_tab_stop
+                       else if c == ' ' then
+                               content_column += 1
+                       else
+                               has_content = true
+                               break
+                       end
+               end
+
+               if in_paragraph then
+                       # if the list item is ordered, then start number must be 1 to interrupt a paragraph
+                       if is_ordered and digit != 1 then
+                               return null
+                       end
+                       # empty list item can not interrupt a paragraph
+                       if not has_content then
+                               return null
+                       end
+               end
+
+               if not has_content or (content_column - column_after_marker) > 4 then
+                       # if this line is blank or has a code block, default to 1 space after marker
+                       content_column = column_after_marker + 1
+               end
+               return new MdListData(is_ordered, bullet, digit, delim, content_column)
+       end
+
+       # Return true if the two list items are of the same type
+       #
+       # With the same delimiter and bullet character.
+       # This is used in agglomerating list items into lists
+       private fun lists_match(a: MdListBlock, b: MdListData): Bool do
+               if a isa MdUnorderedList and not b.is_ordered then
+                       return a.bullet_marker == b.bullet
+               else if a isa MdOrderedList and b.is_ordered then
+                       return a.delimiter == b.delim
+               end
+               return false
+       end
+end
+
+# Parsed list data
+private class MdListData
+
+       var is_ordered: Bool
+
+       var bullet: nullable Char
+
+       var digit: nullable Int
+
+       var delim: nullable Char
+
+       # Column the content start at
+       var content_column: Int
+end
+
+# List items parser
+class MdListItemParser
+       super MdBlockParser
+
+       redef type BLOCK: MdListItem
+       redef var block = new MdListItem(location) is lazy
+
+       # List item content indend
+       var content_indent: Int
+
+       redef fun try_continue(state) do
+               if state.is_blank then
+                       if block.first_child == null then
+                               # blank line after empty list item
+                               return null
+                       end
+                       return new MdBlockContinue.at_index(state.next_non_space_index)
+               end
+               if state.indent >= content_indent then
+                       return new MdBlockContinue.at_column(state.column + content_indent)
+               end
+               return null
+       end
+
+       redef fun parse_inlines(inline_parser) do
+               var last_child = block.last_child
+               if last_child != null then
+                       location.line_end = last_child.location.line_end
+                       location.column_end = last_child.location.column_end
+               end
+       end
+end
+
+# Thematic breaks parser
+class MdThematicBreakParser
+       super MdBlockParser
+
+       redef type BLOCK: MdThematicBreak
+       redef var block = new MdThematicBreak(location, pattern) is lazy
+
+       # Thematic break pattern
+       var pattern: String
+
+       redef fun try_continue(state) do return null
+
+       redef fun finalize(parser) do
+               super
+
+               location.line_end = line_start
+               location.column_end = column_start + pattern.length - 1
+       end
+end
+
+# Thematic breaks parser factory
+class MdThematicBreakParserFactory
+       super MdBlockQuoteParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               if state.indent >= 4 then return null
+
+               var next_non_space = state.next_non_space_index
+               var line = state.line_string
+               var tbreak  = line.substring(next_non_space, line.length - next_non_space).search(re_thematic_break)
+               if tbreak != null then
+                       return (new MdBlockStart(
+                               [new MdThematicBreakParser(
+                                       state.line,
+                                       state.column + 1,
+                                       next_non_space,
+                                       tbreak.to_s)]
+                               )).at_index(line.length)
+               end
+               return null
+       end
+end
+
+# Paragraphs parser
+class MdParagraphParser
+       super MdBlockParser
+
+       redef type BLOCK: MdParagraph
+
+       redef var block = new MdParagraph(location) is lazy
+
+       # Paragraph content
+       var content: nullable Buffer = new Buffer
+
+       redef fun try_continue(state) do
+               if state.is_blank then return null
+               return new MdBlockContinue.at_index(state.index)
+       end
+
+       redef fun add_line(line) do
+               var content = self.content
+               if content == null then return
+               if not content.is_empty then
+                       content.add('\n')
+               end
+               content.append(line)
+       end
+
+       redef fun finalize(parser) do
+               super
+
+               var inline_parser = parser.inline_parser
+               var content = self.content
+               if content == null then return
+
+               var content_string = content.to_s
+               var has_reference_defs = false
+
+               var pos = inline_parser.parse_reference(content_string)
+               # try parsing the beginning as link reference definitions
+               while content_string.length > 3 and content_string.chars[0] == '[' and pos != 0 do
+                       content_string = content_string.substring(pos, content_string.length - pos)
+                       has_reference_defs = true
+                       pos = inline_parser.parse_reference(content_string)
+               end
+
+               if has_reference_defs and content_string.is_blank then
+                       block.unlink
+                       self.content = null
+               else
+                       self.content = new Buffer.from_text(content_string)
+               end
+       end
+
+       redef fun parse_inlines(inline_parser) do
+               var content = self.content
+               if content == null then return
+               inline_parser.parse(content.to_s, content_offset, block)
+
+               var last_child = block.last_child
+               if last_child != null then
+                       location.line_end = last_child.location.line_end
+                       location.column_end = last_child.location.column_end
+               end
+       end
+end
+
+# Html blocks parser
+class MdHtmlBlockParser
+       super MdBlockParser
+
+       redef type BLOCK: MdHtmlBlock
+       redef var block = new MdHtmlBlock(location) is lazy
+
+       # Closing tag pattern
+       #
+       # Or null if the block is not closed
+       var closing_pattern: nullable Pattern
+
+       # Is the current block finished?
+       var finished = false
+
+       # Block content
+       var content = new Buffer
+
+       redef fun try_continue(state) do
+               if finished then return null
+
+               # blank lin ends type 6 and 7 blocks
+               if state.is_blank and closing_pattern == null then return null
+
+               return new MdBlockContinue.at_index(state.index)
+       end
+
+       redef fun add_line(line) do
+               if not content.is_empty then
+                       content.add('\n')
+               end
+               content.append(line)
+               var closing_pattern = self.closing_pattern
+               if closing_pattern != null and line.has(closing_pattern) then
+                       finished = true
+               end
+       end
+
+       redef fun finalize(parser) do
+               super
+
+               var content = self.content.to_s
+               block.literal = content
+
+               var lines = content.split("\n")
+               location.line_end = location.line_start + lines.length - 1
+               location.column_end = lines.last.length
+       end
+end
+
+# Html blocks parser factory
+class MdHtmlBlockParserFactory
+       super MdBlockParserFactory
+
+       redef fun try_start(state, matched_block_parser) do
+               var next_non_space = state.next_non_space_index
+               var line = state.line_string
+
+               if state.indent >= 4 or line.chars[next_non_space] != '<' then return null
+
+               for block_type in [0..6] do
+                       # type 7 can not interrupt a paragraph
+                       if block_type == 6 and matched_block_parser.block isa MdParagraph then continue
+                       var opener = re_html_blocks[block_type].first
+                       var closer = re_html_blocks[block_type].last
+                       if line.substring(next_non_space, line.length - next_non_space).has(opener.as(not null)) then
+                               return (new MdBlockStart(
+                                       [new MdHtmlBlockParser(
+                                               state.line,
+                                               state.column + 1,
+                                               next_non_space,
+                                               closer)])
+                                       ).at_index(state.index)
+                       end
+               end
+               return null
+       end
+end
+
+# Post Processing
+
+# Markdown post processor
+#
+# A Markdown AST visitor called after parsing from a MdParser
+abstract class MdPostProcessor
+       super MdVisitor
+
+       # Document behing processed
+       #
+       # Availlable only during a call to `post_process`.
+       var document: nullable MdDocument = null
+
+       # Post process the `document` parsed by `parser`
+       fun post_process(parser: MdParser, document: MdDocument) do
+               self.document = document
+               enter_visit(document)
+               self.document = null
+       end
+
+       # Call `MdNode::post_process`
+       redef fun visit(node) do node.post_process(self)
+end
+
+redef class MdNode
+
+       # Accept the visit of a `MdPostProcessor`
+       fun post_process(v: MdPostProcessor) do visit_all(v)
+end
+
+# Utils
+
+redef class Sys
+       # ATX headings matching
+       private var re_atx_heading: Regex = "^(#\{1,6\})([ \t]+|$)".to_re
+
+       # ATX trailings matching
+       private var re_atx_trailing: Regex = "(^|[ \t]+)#+[ \t]*$".to_re
+
+       # SeText headings matching
+       private var re_setext_heading: Regex = "^(=+|-+)[ \t]*$".to_re
+
+       # Blank lines matching
+       var re_trailing_blank_lines: Regex = "(\n[ \t]*)+$".to_re
+
+       # Opening fence matching
+       var re_opening_fence: Regex = "^(`\{3,\})(.*)|^(~\{3,\})(.*)".to_re
+
+       # Closing fence matching
+       var re_closing_fence: Regex = "^(`\{3,\}|~\{3,\})( *$)".to_re
+
+       # List marker matching
+       var re_list_marker: Regex = "^([*+-])( |\t|$)|^([0-9]\{1,9\})([.)])( |\t|$)".to_re
+
+       # Thematic break pattern
+       var re_thematic_break: Regex = "^((\\*[ \t]*)\{3,\}|(_[ \t]*)\{3,\}|(-[ \t]*)\{3,\})[ \t]*$".to_re
+
+       # HTML blocks patterns
+       var re_html_blocks: Array[Array[nullable Regex]] do
+               var blocks = new Array[Array[nullable Regex]]
+
+               var re0_opening = "^<(script|pre|style)(\\s|>|$)".to_re
+               re0_opening.ignore_case = true
+               var re0_closing = "</(script|pre|style)>".to_re
+               re0_closing.ignore_case = true
+               blocks.add([re0_opening, re0_closing])
+
+               blocks.add([
+                       "^<!--".to_re,
+                       "-->".to_re
+               ])
+
+               blocks.add([
+                       "^<[?]".to_re,
+                       "\\?>".to_re
+               ])
+
+               blocks.add([
+                       "^<![A-Z]".to_re,
+                       ">".to_re
+               ])
+
+               blocks.add([
+                       "^<!\\[CDATA\\[".to_re,
+                       "\\]\\]>".to_re
+               ])
+
+               var re5_opening = "^</?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(\\s|[/]?[>]|$)".to_re
+               re5_opening.ignore_case = true
+               blocks.add([re5_opening, null])
+
+               var p_tagname = "[A-Za-z][A-Za-z0-9-]*"
+               var p_attribute_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
+               var p_uquoted_value = "[^\"'=<>`\\x00-\\x20]+"
+               var p_squoted_value = "'[^']*'"
+               var p_dquoted_value = "\"[^\"]*\""
+               var p_attribute_value = "({p_uquoted_value}|{p_squoted_value}|{p_dquoted_value})"
+               var p_attribute_value_spec = "(\\s*=\\s*{p_attribute_value})"
+               var p_attribute = "(\\s{p_attribute_name}{p_attribute_value_spec}?)"
+               var p_opentag = "<{p_tagname}{p_attribute}*\\s*/?>"
+               var p_closetag = "</{p_tagname}\\s*[>]"
+               var re6_opening = "^({p_opentag}|{p_closetag})\\s*$".to_re
+               re6_opening.ignore_case = true
+               blocks.add([re6_opening, null])
+
+               return blocks
+       end
+end
+
+redef class Int
+
+       # Tab stop is 4
+       private fun columns_to_next_tab_stop: Int do return 4 - (self % 4)
+end
+
+redef class String
+
+       # Is this string blank?
+       #
+       # i.e. contains only spacing characters.
+       private fun is_blank: Bool do
+               for i in [0 .. length[ do
+                       var c = chars[i]
+                       if c == ' ' or c == '\t' or c == '\n' or c == '\r' then
+                               continue
+                       else
+                               return false
+                       end
+               end
+               return true
+       end
+
+       # Is the character at `index` a space or a tab
+       #
+       # Return false if `index > self.length`.
+       private fun is_space_or_tab(index: Int): Bool do
+               if index >= length then return false
+               var c = chars[index]
+               return c == ' ' or c == '\t'
+       end
+end
diff --git a/lib/markdown2/markdown_github.nit b/lib/markdown2/markdown_github.nit
new file mode 100644 (file)
index 0000000..2239953
--- /dev/null
@@ -0,0 +1,117 @@
+# 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 Github mode
+#
+# Enables:
+# * strike processing: ~strike~
+# * super processing: ^super^
+#
+# TODO table
+# TODO todo lists
+module markdown_github
+
+intrude import markdown_inline_parsing
+intrude import markdown_block_parsing
+
+redef class MdParser
+
+       # Enable Github mode
+       var github_mode = false is writable
+
+       redef var inline_parser is lazy do
+               var parser = super
+               parser.github_mode = github_mode
+               return parser
+       end
+end
+
+redef class MdInlineParser
+
+       # Enable Github mode
+       private var github_mode = false
+
+       redef var delimiter_processors is lazy do
+               var delimiters = super
+               if github_mode then
+                       delimiters.add new MdStrikeProcessor
+                       delimiters.add new MdSuperProcessor
+               end
+               return delimiters
+       end
+end
+
+# Strike processor
+class MdStrikeProcessor
+       super MdEmphasisDelimiterProcessor
+       noautoinit
+
+       redef var delimiter_char = '~'
+       redef var min_length = 1
+       redef fun delimiter_use(opener, closer) do return opener.original_length
+
+       redef fun process(opener, closer, delimiter_use) do
+               var node = new MdStrike(
+                               new MdLocation(
+                                       opener.location.line_start,
+                                       opener.location.column_start,
+                                       closer.location.line_end,
+                                       closer.location.column_end),
+                       opening_delimiter.to_s * delimiter_use)
+               var tmp = opener.next
+               while tmp != null and tmp != closer do
+                       var next = tmp.next
+                       node.append_child(tmp)
+                       tmp = next
+               end
+               opener.insert_after(node)
+       end
+end
+
+#  Striked text
+class MdStrike
+       super MdDelimited
+end
+
+# Super processor
+class MdSuperProcessor
+       super MdEmphasisDelimiterProcessor
+       noautoinit
+
+       redef var delimiter_char = '^'
+       redef var min_length = 1
+       redef fun delimiter_use(opener, closer) do return opener.original_length
+
+       redef fun process(opener, closer, delimiter_use) do
+               var node = new MdSuper(
+                       new MdLocation(
+                               opener.location.line_start,
+                               opener.location.column_start,
+                               closer.location.line_end,
+                               closer.location.column_end),
+                       opening_delimiter.to_s * delimiter_use)
+               var tmp = opener.next
+               while tmp != null and tmp != closer do
+                       var next = tmp.next
+                       node.append_child(tmp)
+                       tmp = next
+               end
+               opener.insert_after(node)
+       end
+end
+
+# Super text
+class MdSuper
+       super MdDelimited
+end
diff --git a/lib/markdown2/markdown_html_rendering.nit b/lib/markdown2/markdown_html_rendering.nit
new file mode 100644 (file)
index 0000000..11addf6
--- /dev/null
@@ -0,0 +1,458 @@
+# 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.
+
+# HTML rendering of Markdown documents
+module markdown_html_rendering
+
+import markdown_rendering
+import markdown_github
+import markdown_wikilinks
+
+# Markdown document renderer to HTML
+class HtmlRenderer
+       super MdRenderer
+
+       # HTML output under construction
+       private var html: Buffer is noinit
+
+       # Render `document` as HTML
+       redef fun render(document) do
+               reset
+               enter_visit(document)
+               return html.write_to_string
+       end
+
+       redef fun visit(node) do node.render_html(self)
+
+       # Reset `headings` and internal state
+       fun reset do
+               html = new Buffer
+               if enable_heading_ids then headings.clear
+       end
+
+       # Last char visited
+       #
+       # Used to avoid double blank lines.
+       private var last_char: nullable Char = null
+
+       # Add `string` to `html`
+       private fun add(string: String) do
+               html.append(string)
+               if not html.is_empty then
+                       last_char = html.last
+               end
+       end
+
+       # Add a raw `html` string to the output
+       #
+       # Raw means that the string will not be escaped.
+       fun add_raw(html: String) do add html
+
+       # Add `text` string to the output
+       #
+       # The string will be escaped.
+       fun add_text(text: String) do add html_escape(text, true)
+
+       # Add a blank line to the output
+       fun add_line do
+               if last_char != null and last_char != '\n' then
+                       add "\n"
+               end
+       end
+
+       # Escape `string` to HTML
+       #
+       # When `keep_entities`, HTML entities will not be escaped.
+       fun html_escape(string: String, keep_entities: Bool): String do
+               var buf: nullable Buffer = null
+               for i in [0..string.length[ do
+                       var c = string.chars[i]
+                       var sub = null
+                       if c == '&' and (not keep_entities or string.search_from(re_entity, i) == null) then
+                               sub = "&amp;"
+                       else if c == '<' then
+                               sub = "&lt;"
+                       else if c == '>' then
+                               sub = "&gt;"
+                       else if c == '"' then
+                               sub = "&quot;"
+                       else
+                               if buf != null then buf.add c
+                               continue
+                       end
+                       if buf == null then
+                               buf = new Buffer
+                               for j in [0..i[ do buf.add string.chars[j]
+                       end
+                       buf.append sub
+               end
+
+               if buf == null then return string
+               return buf.to_s
+       end
+
+       # HTML entity pattern
+       private var re_entity: Regex = "^&(#x[a-f0-9]\{1,8\}|#[0-9]\{1,8\}|[a-z][a-z0-9]\{1,31\});".to_re
+
+       # Encode the `uri` string
+       fun encode_uri(uri: String): String do
+               var buf = new Buffer
+
+               var i = 0
+               while i < uri.length do
+                       var c = uri.chars[i]
+                       if (c >= '0' and c <= '9') or
+                          (c >= 'a' and c <= 'z') or
+                          (c >= 'A' and c <= 'Z') or
+                          c == ';' or c == ',' or c == '/' or c == '?' or
+                          c == ':' or c == '@' or c == '=' or c == '+' or
+                          c == '$' or c == '-' or c == '_' or c == '.' or
+                          c == '!' or c == '~' or c == '*' or c == '(' or
+                          c == ')' or c == '#' or c == '\''
+                       then
+                               buf.add c
+                       else if c == '&' then
+                               buf.append "&amp;"
+                       else if c == '%' and uri.search_from(re_uri_code, i) != null then
+                               buf.append uri.substring(i, 3)
+                               i += 2
+                       else
+                               var bytes = c.to_s.bytes
+                               for b in bytes do buf.append "%{b.to_i.to_hex}".to_upper
+                       end
+                       i += 1
+               end
+
+               return buf.to_s
+       end
+
+       # URI encode pattern
+       private var re_uri_code: Regex = "^%[a-zA-Z0-9]\{2\}".to_re
+
+       # Add `id` tags to headings
+       var enable_heading_ids = false is optional, writable
+
+       # Associate headings ids to blocks
+       var headings = new ArrayMap[String, MdHeading]
+
+       # Strip heading id
+       fun strip_id(text: String): String do
+               # strip id
+               var b = new FlatBuffer
+               for c in text do
+                       if c == ' ' then
+                               b.add '_'
+                       else
+                               if not c.is_letter and
+                                  not c.is_digit and
+                                  not allowed_id_chars.has(c) then continue
+                               b.add c
+                       end
+               end
+               var res = b.to_s
+               if res.is_empty then res = "_"
+               var key = res
+               # check for multiple id definitions
+               if headings.has_key(key) then
+                       var i = 1
+                       key = "{res}_{i}"
+                       while headings.has_key(key) do
+                               i += 1
+                               key = "{res}_{i}"
+                       end
+               end
+               return key
+       end
+
+       # Allowed characters in ids
+       var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
+end
+
+redef class MdNode
+
+       # Render `self` as HTML
+       fun render_html(v: HtmlRenderer) do visit_all(v)
+end
+
+# Blocks
+
+redef class MdBlockQuote
+       redef fun render_html(v) do
+               v.add_line
+               v.add_raw "<blockquote>"
+               v.add_line
+               visit_all(v)
+               v.add_line
+               v.add_raw "</blockquote>"
+               v.add_line
+       end
+end
+
+redef class MdCodeBlock
+       redef fun render_html(v) do
+               var info = self.info
+               v.add_line
+               v.add_raw "<pre>"
+               v.add_raw "<code"
+               if info != null and not info.is_empty then
+                       v.add_raw " class=\"language-{info.split(" ").first}\""
+               end
+               v.add_raw ">"
+               var literal = self.literal or else ""
+               var lines = literal.split("\n")
+               for i in [0..lines.length[ do
+                       var line = lines[i]
+                       v.add_raw v.html_escape(line, false)
+                       if i < lines.length - 1 then
+                               v.add_raw "\n"
+                       end
+               end
+               v.add_raw "</code>"
+               v.add_raw "</pre>"
+               v.add_line
+       end
+end
+
+redef class MdHeading
+       redef fun render_html(v) do
+               v.add_line
+               if v.enable_heading_ids then
+                       var id = self.id
+                       if id == null then
+                               id = v.strip_id(title)
+                               v.headings[id] = self
+                               self.id = id
+                       end
+                       v.add_raw "<h{level} id=\"{id}\">"
+               else
+                       v.add_raw "<h{level}>"
+               end
+               visit_all(v)
+               v.add_raw "</h{level}>"
+               v.add_line
+       end
+
+       #
+       var id: nullable String = null
+
+       #
+       fun title: String do
+               var v = new RawTextVisitor
+               return v.render(self)
+       end
+end
+
+redef class MdUnorderedList
+       redef fun render_html(v) do
+               v.add_line
+               v.add_raw "<ul>"
+               v.add_line
+               visit_all(v)
+               v.add_line
+               v.add_raw "</ul>"
+               v.add_line
+       end
+end
+
+redef class MdOrderedList
+       redef fun render_html(v) do
+               var start = self.start_number
+               v.add_line
+               v.add_raw "<ol"
+               if start != 1 then
+                       v.add_raw " start=\"{start}\""
+               end
+               v.add_raw ">"
+               v.add_line
+               visit_all(v)
+               v.add_line
+               v.add_raw "</ol>"
+               v.add_line
+       end
+end
+
+redef class MdListItem
+       redef fun render_html(v) do
+               v.add_raw "<li>"
+               visit_all(v)
+               v.add_raw "</li>"
+               v.add_line
+       end
+end
+
+redef class MdParagraph
+       redef fun render_html(v) do
+               var is_tight = is_in_tight_list
+               if not is_tight then
+                       v.add_line
+                       v.add_raw "<p>"
+               end
+               visit_all(v)
+               if not is_tight then
+                       v.add_raw "</p>"
+                       v.add_line
+               end
+       end
+end
+
+redef class MdThematicBreak
+       redef fun render_html(v) do
+               v.add_line
+               v.add_raw "<hr />"
+               v.add_line
+       end
+end
+
+redef class MdHtmlBlock
+       redef fun render_html(v) do
+               v.add_line
+               var literal = self.literal or else ""
+               var lines = literal.split("\n")
+               for i in [0..lines.length[ do
+                       var line = lines[i]
+                       if not line.trim.is_empty then
+                               v.add_raw line
+                       end
+                       if i < lines.length - 1 then
+                               v.add_raw "\n"
+                       end
+               end
+               v.add_line
+       end
+end
+
+# Inlines
+
+redef class MdHardLineBreak
+       redef fun render_html(v) do
+               v.add_raw "<br />"
+               v.add_line
+       end
+end
+
+redef class MdSoftLineBreak
+       redef fun render_html(v) do
+               v.add_raw "\n"
+       end
+end
+
+redef class MdCode
+       redef fun render_html(v) do
+               v.add_raw "<code>"
+               v.add_raw v.html_escape(literal, false)
+               v.add_raw "</code>"
+       end
+end
+
+redef class MdEmphasis
+       redef fun render_html(v) do
+               v.add_raw "<em>"
+               visit_all(v)
+               v.add_raw "</em>"
+       end
+end
+
+redef class MdStrongEmphasis
+       redef fun render_html(v) do
+               v.add_raw "<strong>"
+               visit_all(v)
+               v.add_raw "</strong>"
+       end
+end
+
+redef class MdHtmlInline
+       redef fun render_html(v) do
+               v.add_raw literal
+       end
+end
+
+redef class MdImage
+       redef fun render_html(v) do
+               var url = self.destination
+               var title = self.title
+               v.add_raw "<img"
+               v.add_raw " src=\"{v.encode_uri(url)}\""
+
+               var alt_text = self.alt_text
+               v.add_raw " alt=\"{alt_text}\""
+
+               if title != null and not title.is_empty then
+                       v.add_raw " title=\""
+                       v.add_text title
+                       v.add_raw "\""
+               end
+
+               v.add_raw " />"
+       end
+
+       private fun alt_text: String do
+               var v = new RawTextVisitor
+               return v.render(self)
+       end
+end
+
+redef class MdLink
+       redef fun render_html(v) do
+               var url = self.destination
+               var title = self.title
+               v.add_raw "<a"
+               v.add_raw " href=\"{v.encode_uri(url)}\""
+               if title != null and not title.is_empty then
+                       v.add_raw " title=\""
+                       v.add_text title
+                       v.add_raw "\""
+               end
+               v.add_raw ">"
+               visit_all(v)
+               v.add_raw "</a>"
+       end
+end
+
+redef class MdText
+       redef fun render_html(v) do
+               v.add_text literal
+       end
+end
+
+# Github mode
+
+redef class MdStrike
+       redef fun render_html(v) do
+               v.add_raw "<del>"
+               visit_all(v)
+               v.add_raw "</del>"
+       end
+end
+
+redef class MdSuper
+       redef fun render_html(v) do
+               v.add_raw "<sup>"
+               visit_all(v)
+               v.add_raw "</sup>"
+       end
+end
+
+# Wikilinks mode
+
+redef class MdWikilink
+
+       # Dummy rendering of wikilinks
+       #
+       # Clients should redefine this.
+       redef fun render_html(v) do
+               v.add_raw "<wiki link=\"{v.encode_uri(link)}\">"
+               visit_all(v)
+               v.add_raw "</wiki>"
+       end
+end
diff --git a/lib/markdown2/markdown_inline_parsing.nit b/lib/markdown2/markdown_inline_parsing.nit
new file mode 100644 (file)
index 0000000..703f46e
--- /dev/null
@@ -0,0 +1,1401 @@
+# 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.
+
+# Parser for inline markdown
+#
+# Used to create the AST representation of inline nodes like emphasis, code, links
+# images etc.
+module markdown_inline_parsing
+
+import markdown_ast
+
+# Parser for inline content (text, links, emphasis, etc)
+class MdInlineParser
+
+       # List of delimiter processors to use
+       private var delimiter_processors: Array[MdDelimiterProcessor] is lazy do
+               var delimiters = new Array[MdDelimiterProcessor]
+               delimiters.add new MdAsteriskDelimiterProcessor
+               delimiters.add new MdUnderscoreDelimiterProcessor
+               return delimiters
+       end
+
+       # Map special characters to their delimiter processor
+       private var delimiter_processors_map: Map[Char, MdDelimiterProcessor] is lazy do
+               var map = new HashMap[Char, MdDelimiterProcessor]
+               for delimiter_processor in delimiter_processors do
+                       add_delimiter_processor(delimiter_processor, map)
+               end
+               special_characters.add_all map.keys
+               return map
+       end
+
+       # Register a delimiter processor
+       private fun add_delimiter_processor(delimiter_processor: MdDelimiterProcessor, map: Map[Char, MdDelimiterProcessor]) do
+               var opening = delimiter_processor.opening_delimiter
+               var closing = delimiter_processor.closing_delimiter
+               if opening == closing then
+                       if map.has_key(opening) then
+                               var old = map[opening]
+                               if old.opening_delimiter == old.closing_delimiter then
+                                       var s: MdStaggeredDelimiterProcessor
+                                       if old isa MdStaggeredDelimiterProcessor then
+                                               s = old
+                                       else
+                                               s = new MdStaggeredDelimiterProcessor(opening)
+                                               s.add old
+                                       end
+                                       s.add delimiter_processor
+                                       map[opening] = s
+                               else
+                                       add_delimiter_processor_for_char(opening, delimiter_processor, map)
+                               end
+                       else
+                               add_delimiter_processor_for_char(opening, delimiter_processor, map)
+                       end
+               else
+                       add_delimiter_processor_for_char(opening, delimiter_processor, map)
+                       add_delimiter_processor_for_char(closing, delimiter_processor, map)
+               end
+       end
+
+       # Register a delimiter processor for a special character
+       private fun add_delimiter_processor_for_char(delimiter_char: Char, delimiter_processor: MdDelimiterProcessor, map: Map[Char, MdDelimiterProcessor]) do
+               assert not map.has_key(delimiter_char) else
+                       print "Delimiter processor conflict with delimiter char `{delimiter_char}`"
+               end
+               map[delimiter_char] = delimiter_processor
+       end
+
+       # List of characters that have a special Markdown meaning
+       private var special_characters: Array[Char] = ['\n', '`', '[', ']', '\\', '!', '<', '&']
+
+       # Link references by ID, needs to be built up using `parse_reference` before calling `parse`
+       private var reference_map = new HashMap[String, MdLink]
+
+       # Current block under parsing
+       private var block: MdNode is noinit
+
+       # Current input string
+       private var input: String is noinit
+
+       # Current index
+       private var index: Int is noinit
+
+       # Current line
+       private var line: Int is noinit
+
+       # Current column
+       private var column: Int is noinit
+
+       # Current column offset
+       private var column_offset: Int is noinit
+
+       # Top delimiter (emphasis, strong emphasis or custom emphasis)
+       # Brackets are on a separate stack, different from the algorithm described in the spec.
+       private var last_delimiter: nullable MdDelimiter = null
+
+       # Top opening bracket (`[` or `![`)
+       private var last_bracket: nullable MdBracket = null
+
+       # Parse `input` as inline and add resulting nodes as children to `block`
+       fun parse(input: String, offset: Int, block: MdNode) do
+               self.block = block
+               self.input = input.trim
+               self.index = 0
+               self.last_delimiter = null
+               self.last_bracket = null
+               self.line = block.location.line_start
+               self.column_offset = offset
+               self.column = 1 + column_offset
+
+               var more_to_parse = parse_inline
+               while more_to_parse do
+                       more_to_parse = parse_inline
+               end
+
+               process_delimiters(null)
+               merge_child_text_nodes(block)
+       end
+
+       # Advance the current index of `count` characters
+       private fun advance(count: Int) do
+               index += count
+               column += count
+       end
+
+       # Attempt to parse a link reference
+       #
+       # Return how many characters were parsed as a reference.
+       # Returns 0 if none.
+       fun parse_reference(input: String): Int do
+               self.input = input
+               self.index = 0
+               self.column = 0
+               var dest
+               var title
+               var match_chars
+               var start_index = index
+
+               # label
+               match_chars = parse_link_label
+               if match_chars == 0 then return 0
+               advance match_chars
+
+               var raw_label = input.substring(0, match_chars)
+
+               # colon
+               if peek != ':' then return 0
+               advance 1
+
+               # link url
+               spnl
+
+               dest = parse_link_destination.first
+               if dest == null or dest.is_empty then return 0
+
+               var before_title = index
+               var before_column = column
+               spnl
+               title = parse_link_title
+               if title == null then
+                       # rewind before spaces
+                       index = before_title
+                       column = before_column
+               end
+
+               var at_line_end = true
+               if index != input.length and match(re_line_end) == null then
+                       if title == null then
+                               at_line_end = false
+                       else
+                               # the potential title we found is not at the line end,
+                               # but it could still be a legal link reference if we discard the title
+                               title = null
+                               # rewind before spaces
+                               index = before_title
+                               column = before_column
+                               # and instead check if the link URL is at the line end
+                               at_line_end = match(re_line_end) != null
+                       end
+               end
+
+               if not at_line_end then return 0
+
+               var normalized_label = raw_label.normalize_reference
+               if normalized_label.is_empty then return 0
+
+               if not reference_map.has_key(normalized_label) then
+                       var link = new MdLink(new MdLocation(0, 0, 0, 0), dest, title)
+                       reference_map[normalized_label] = link
+               end
+
+               return index - start_index
+       end
+
+       # Line end pattern
+       private var re_line_end: Regex = "^ *(\n|$)".to_re
+
+       # Append standard text to the current block
+       #
+       # Read `text` between `begin_index` and `end_index`.
+       private fun append_text(text: String, begin_index, end_index: nullable Int): MdText do
+               var node: MdText
+               if begin_index != null and end_index != null then
+                       var nb_chars = end_index - begin_index
+                       var string = text.substring(begin_index, nb_chars)
+                       node = new MdText(
+                               new MdLocation(
+                                       line,
+                                       column,
+                                       line,
+                                       column + nb_chars - 1
+                               ), string)
+               else
+                       node = new MdText(
+                               new MdLocation(
+                                       line,
+                                       column,
+                                       line,
+                                       column + text.length
+                               ), text)
+               end
+               append_node(node)
+               return node
+       end
+
+       # Append `node` to the current block
+       private fun append_node(node: MdNode) do block.append_child(node)
+
+       # Parse the next inline element in subject, advancing input index
+       #
+       # On success, add the result to block's children and return true.
+       # On failure, return false.
+       private fun parse_inline: Bool do
+               var res: Bool
+               var c = peek
+               if c == '\0' then return false
+               if c == '\n' then
+                       res = parse_newline
+               else if c == '\\' then
+                       res = parse_backslash
+               else if c == '`' then
+                       res = parse_backticks
+               else if c == '[' then
+                       res = parse_open_bracket
+               else if c == '!' then
+                       res = parse_bang
+               else if c == ']' then
+                       res = parse_close_bracket
+               else if c == '<' then
+                       res = parse_auto_link or parse_html_inline
+               else if c == '&' then
+                       res = parse_entity
+               else
+                       if delimiter_processors_map.has_key(c) then
+                               res = parse_delimiters(delimiter_processors_map[c], c)
+                       else
+                               res = parse_string
+                       end
+               end
+
+               if not res then
+                       advance 1
+                       # When we get here, it's only for a single special character that turned
+                       # out to not have a special meaning.
+                       # So we shouldn't have a single surrogate here, hence it should be ok
+                       # to turn it into a String
+                       var literal = c.to_s
+                       append_text(literal)
+               end
+
+               return true
+       end
+
+       # If `re` matches at current index in the input, advance index and return the match
+       # Else return null.
+       private fun match(re: Pattern): nullable String do
+               if index >= input.length then return null
+               var match = input.search_from(re, index)
+               if match != null then
+                       index = match.after
+                       column = match.after
+                       return match.to_s
+               end
+               return null
+       end
+
+       # Return the char at the current input index, or `\0`
+       private fun peek: Char do
+               if index < input.length then
+                       return input.chars[index]
+               end
+               return '\0'
+       end
+
+       # Return the char at the current input index + 1, or `\0`
+       private fun peek_next: Char do
+               if index + 1 < input.length then
+                       return input.chars[index + 1]
+               end
+               return '\0'
+       end
+
+       # Parse zero or more space characters, incuding at most one newline
+       private fun spnl: Bool do
+               var found_nl = false
+               loop
+                       var c = peek
+                       if c == ' ' or c == '\t' then
+                               advance 1
+                               continue
+                       else if c == '\n' then
+                               if found_nl then break
+                               found_nl = true
+                               advance 1
+                               continue
+                       end
+                       break
+               end
+               return true
+       end
+
+       # Parse a new line
+       #
+       # If it was preceded by two spaces, return a hard line break,
+       # otherwise a soft line break
+       private fun parse_newline: Bool do
+               advance 1 # assume we're at a `\n`
+
+               var last_child = block.last_child
+
+               # check previous text for trailing spaces
+               # the `has_suffix` is an optimization to avoid an RE match in the common case
+               if last_child != null and last_child isa MdText and
+                  (last_child.literal.has_suffix(" ")) then
+                       var text = last_child
+                       var literal = text.literal
+                       var match = literal.search(re_final_space)
+                       var spaces = if match != null then match.length else 0
+                       if spaces > 0 then
+                               text.literal = literal.substring(0, literal.length - spaces)
+                       end
+                       last_child.location.column_end = last_child.location.column_end - spaces
+                       if spaces >= 2 then
+                               append_node(new MdHardLineBreak(new MdLocation(line, column - spaces - 1, line, column - 1), false))
+                       else
+                               append_node(new MdSoftLineBreak(new MdLocation(line, column - spaces - 1, line, column -1)))
+                       end
+               else
+                       append_node(new MdSoftLineBreak(new MdLocation(line, column - 1, line, column - 1)))
+               end
+               line += 1
+               column = 1 + column_offset
+
+               # gobble leading spaces in next line
+               while peek == ' ' do
+                       advance 1
+               end
+               return true
+       end
+
+       # Final white spaces pattern
+       private var re_final_space: Regex = " *$".to_re
+
+       # Parse a backslash-escaped special character
+       #
+       # Add either the escaped characters, a hard line break (if the backslash is followed by
+       # a new line), or a literal backslash to the block's children.
+       private fun parse_backslash: Bool do
+               advance 1
+               if peek == '\n' then
+                       append_node(new MdHardLineBreak(new MdLocation(line, column - 1, line, column), true))
+                       advance 1
+                       line += 1
+                       column = 1 + column_offset
+               else if index < input.length and input.substring(index, 1).has(re_escapable) then
+                   append_text(input, index, index + 1)
+                   advance 1
+               else
+                       append_text("\\")
+               end
+               return true
+       end
+
+       # Escapable characters pattern
+       private var p_escapable = "[]!\"#$%&\'()*+,./:;<=>?@\\[\\\\^_`\\\{|\\\}~-]"
+
+       # Escapable characters regex
+       private var re_escapable: Regex = "^{p_escapable}".to_re
+
+       # Attempt to parse backticks
+       #
+       # Adding either a backtick code span or a literal sequence of backticks.
+       private fun parse_backticks: Bool do
+               var column_before = column
+               var ticks = match(re_ticks_here)
+               if ticks == null then return false
+
+               var after_open_ticks = index
+               var matched = match(re_ticks)
+               while matched != null do
+                       if matched == ticks then
+                               var content = input.substring(after_open_ticks, index - after_open_ticks - ticks.length)
+                               content = content.trim
+                               content = content.replace(re_whitespace, " ")
+                               var node = new MdCode(new MdLocation(line, column_before, line, column), matched.to_s, content.trim)
+                               append_node(node)
+                               column += 1
+                               return true
+                       end
+                       matched = match(re_ticks)
+               end
+               # If we got here, we didn't match a closing backtick sequence
+               index = after_open_ticks
+               column = after_open_ticks + 1
+               append_text(ticks)
+               return true
+       end
+
+       # Backticks starting pattern
+       private var re_ticks_here: Regex = "^`+".to_re
+
+       # Backticks pattern
+       private var re_ticks: Regex = "`+".to_re
+
+       # Attempt to parse delimiters like emphasis, strong emphasis or custom delimiters
+       private fun parse_delimiters(delimiter_processor: MdDelimiterProcessor, delimiter_char: Char): Bool do
+               var res = scan_delimiters(delimiter_processor, delimiter_char)
+               if res == null then return false
+
+               var length = res.count
+               var start_index = index
+               var start_column = column
+
+               advance length
+               var column_before = column
+               column = start_column
+               var node = append_text(input, start_index, index)
+               column = column_before
+
+               # Add entry to stack for this opener
+               var last_delimiter = new MdDelimiter(node, delimiter_char, res.can_open, res.can_close, last_delimiter)
+               last_delimiter.length = length
+               last_delimiter.original_length = length
+
+               var prev = last_delimiter.prev
+               if prev != null then
+                       prev.next = last_delimiter
+               end
+               self.last_delimiter = last_delimiter
+               return true
+       end
+
+       # Add open bracket to delimiter stack and add a text node to block's children
+       private fun parse_open_bracket: Bool do
+               var start_index = index
+               advance 1
+
+               var node = append_text("[")
+
+               # Add entry to stack for this opener
+               add_bracket(new MdBracket.link(node, start_index, column - 1, last_bracket, last_delimiter))
+               return true
+       end
+
+       # If next character is `[`, add `!` delimiter to delimiter stack and add a text node to
+       # block's children.
+       # Otherwise just add a text node.
+       private fun parse_bang: Bool do
+               var start_index = index
+               advance 1
+
+               if peek == '[' then
+                       advance 1
+                       var node = append_text("![")
+
+                       # Add entry to stack for this opener
+                       add_bracket(new MdBracket.image(node, start_index + 1, column - 2, last_bracket, last_delimiter))
+               else
+                       append_text("!")
+               end
+               return true
+       end
+
+       # Try match close bracket against an opening delimiter stack
+       #
+       # Add either a link or image, or a plan `[` character, to block's children.
+       # If there is a matching delimiter, remove it from the delimiter stack.
+       private fun parse_close_bracket: Bool do
+               advance 1
+               var start_index = index
+               var start_column = column
+
+               # Get previous `[` or `![`
+               var opener = last_bracket
+               if opener == null then
+                       # no matching opener, just return a literal
+                       append_text("]")
+                       return true
+               end
+
+               if not opener.allowed then
+                       # matching opener but it's not allowed, juste return a literal
+                       append_text("]")
+                       remove_last_bracket
+                       return true
+               end
+
+               # check to see if we have a link or image
+               var dest: nullable Couple[nullable String, Bool] = null
+               var title = null
+               var is_link_or_image = false
+
+               # maybe an inline link like `[foo](\uri "title")`
+               if peek == '(' then
+                       advance 1
+                       spnl
+                       dest = parse_link_destination
+                       if dest.first != null then
+                               spnl
+                               # title needs a whitespace before
+                               if input.substring(index - 1, 1).has(re_whitespace) then
+                                       title = parse_link_title
+                                       spnl
+                               end
+                               if peek == ')' then
+                                       advance 1
+                                       is_link_or_image = true
+                               else
+                                       index = start_index
+                                       column = start_column
+                               end
+                       end
+               end
+
+               # maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]`
+               if not is_link_or_image then
+                       # see if there's a link label like `[bar]` or `[]`
+                       var before_label = index
+                       var label_length = parse_link_label
+                       advance label_length
+                       var ref = null
+                       if label_length > 2 then
+                               ref = input.substring(before_label, label_length)
+                       else if not opener.bracket_after then
+                               # If the second label is empty `[foo][]` or missing `[foo]`, then the first label
+                               # is the reference.
+                               # But it can only be a reference when there's no (unescaped) bracket in it.
+                               # If there is, we don't even need to try to lookup the reference.
+                               ref = input.substring(opener.index, start_index - opener.index)
+                       end
+
+                       if ref != null then
+                               var nref = ref.normalize_reference
+                               if reference_map.has_key(nref) then
+                                       var link = reference_map[nref]
+                                       dest = new Couple[nullable String, Bool](link.destination, false)
+                                       title = link.title
+                                       is_link_or_image = true
+                               end
+                       end
+               end
+
+               if is_link_or_image then
+                       # If we got here, open is a potential opener
+                       var link_or_image: MdLinkOrImage
+                       if opener.is_image then
+                               link_or_image = new MdImage(new MdLocation(line, opener.column, line, column - 1), dest.as(not null).first or else "", title)
+                       else
+                               link_or_image = new MdLink(new MdLocation(line, opener.column, line, column - 1), dest.as(not null).first or else "", title)
+                       end
+                       link_or_image.has_brackets = dest.as(not null).second
+
+                       var node = opener.node.next
+                       while node != null do
+                               var next = node.next
+                               link_or_image.append_child(node)
+                               node = next
+                       end
+                       append_node(link_or_image)
+
+                       # Process delimiters such as emphasis inside a link/image
+                       process_delimiters(opener.prev_delimiter)
+                       merge_child_text_nodes(link_or_image)
+                       # We don't need the corresponding text node anymore, we turned it into a node
+                       opener.node.unlink
+                       remove_last_bracket
+
+                       # Links within links are not allowed
+                       # We found this link, so there can be no other link around it.
+                       if not opener.is_image then
+                               var bracket = last_bracket
+                               while bracket != null do
+                                       if not bracket.is_image then
+                                               # disallow link opener
+                                               bracket.allowed = false
+                                       end
+                                       bracket = bracket.prev
+                               end
+                       end
+                       return true
+               end
+
+               if not is_link_or_image then
+                       if parse_wikilink then return true
+               end
+
+               # no link or image
+               append_text("]")
+               remove_last_bracket
+               index = start_index
+               column = start_column
+               return true
+       end
+
+       # Whitespace pattern
+       private var re_whitespace: Regex = "\\s+".to_re
+
+       # Add a bracket token on top of the `last_bracket` stack
+       private fun add_bracket(bracket: MdBracket) do
+               var last_bracket = self.last_bracket
+               if last_bracket != null then
+                       last_bracket.bracket_after = true
+               end
+               self.last_bracket = bracket
+       end
+
+       # Remove the last bracket on the `last_bracket` stack
+       private fun remove_last_bracket do
+               var last_bracket = self.last_bracket
+               if last_bracket == null then return
+               self.last_bracket = last_bracket.prev
+       end
+
+       # Wikilink placeholder
+       #
+       # Will be defined in sub module.
+       private fun parse_wikilink: Bool do return false
+
+       # Attempt to parse a link destination, returning the string or null if not match
+       private fun parse_link_destination: Couple[nullable String, Bool] do
+               var buffer = new Buffer
+
+               var c = peek
+               var parens = 0
+
+               var has_bracket = c == '<'
+               if has_bracket then advance 1
+
+               loop
+                       c = peek
+                       if c == '\0' then
+                               break # end of input
+                       else if c == ' ' or c == '\t' or c == '\n' or c == '\r' then
+                               break # no spaces allowed in urls
+                       else if c == '\\' then
+                               var next = peek_next
+                               if escapable.has(next) then
+                                       buffer.add next
+                                       advance 2 # skip over the backslash
+                                       continue
+                               end
+                       else if has_bracket and c == '>' then
+                               advance 1
+                               break
+                       else if not has_bracket and c == '(' then
+                               parens += 1
+                       else if not has_bracket and c == ')' then
+                               if parens == 0 then break
+                               parens -= 1
+                       else if c == '\0' then
+                               break
+                       end
+                       buffer.add c
+                       advance 1
+               end
+               return new Couple[nullable String, Bool](buffer.to_s, has_bracket)
+       end
+
+       # Attempt to parse a link title (sans quotes), returning the string or null if no match
+       private fun parse_link_title: nullable String do
+               var c = peek
+               if c != '\'' and c != '"' and c != '(' then
+                       return null
+               end
+               var opener = c
+
+               var buffer = new Buffer
+               loop
+                       advance 1
+                       c = peek
+                       if c == opener or (opener == '(' and c == ')') then
+                               advance 1
+                               break
+                       else if c == '\\' then
+                               var next = peek_next
+                               if escapable.has(next) then
+                                       buffer.add next
+                                       advance 1
+                                       continue
+                               end
+                       else if c == '\0' then
+                               return null
+                       end
+                       buffer.add c
+               end
+               return buffer.to_s
+       end
+
+       # Escapable characters
+       private var escapable = "[]!\"#$%&\'()*+,./:;<=>?@\\^_`\{|\}~-"
+
+       # Attempt to parse a link label returning number of characters parsed
+       private fun parse_link_label: Int do
+               var i = index
+               while i < input.length do
+                       var c = input[i]
+                       if i == index and c != '[' then
+                               return 0
+                       else if c == '[' and i != index then
+                               if input[i - 1] != '\\' or (i - 2 > index and input[i - 2] == '\\') then
+                                       return 0
+                               end
+                       else if c == ']' then
+                               if i > 1001 then return 0
+                               if input[i - 1] != '\\' or (i - 2 > index and input[i - 2] == '\\') then
+                                       return (i - index) + 1
+                               end
+                       end
+                       i += 1
+               end
+               return 0
+       end
+
+       # Attempt to parse an autolink (URL or email in pointy brackets)
+       private fun parse_auto_link: Bool do
+               var column_before = column
+               var m = match(re_autolink_email)
+               if m != null then
+                       var dest = m.substring(1, m.length - 2)
+                       var node = new MdLink(new MdLocation(line, column_before, line, column), "mailto:{dest}", null, true)
+                       node.append_child(new MdText(new MdLocation(line, column_before + 1, line, column - 1), dest))
+                       column += 1
+                       append_node(node)
+                       return true
+               end
+               m = match(re_autolink_url)
+               if m != null then
+                       var dest = m.substring(1, m.length - 2)
+                       var node = new MdLink(new MdLocation(line, column_before, line, column), dest, null, true)
+                       node.append_child(new MdText(new MdLocation(line, column_before + 1, line, column - 1), dest))
+                       column += 1
+                       append_node(node)
+                       return true
+               end
+               return false
+       end
+
+       # Autolink email pattern
+       private var re_autolink_email: Regex = "^<([a-zA-Z0-9.!#$%&'*+/=?^_`\{|\}~-]+@[a-zA-Z0-9]([a-zA-Z0-9-]\{0,61\}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]\{0,61\}[a-zA-Z0-9])?)*)>".to_re
+
+       # Autolink url pattern
+       private var re_autolink_url: Regex = "^<[a-zA-Z][a-zA-Z0-9.+-]\{1,31\}:[^<> ]*>".to_re
+
+       # Attempt to parse an inline HTML string
+       private fun parse_html_inline: Bool do
+               var column_before = column
+               var m = match(re_html_tag)
+               if m != null then
+                       var node = new MdHtmlInline(new MdLocation(line, column_before, line, column), m)
+                       column += 1
+                       append_node(node)
+                       return true
+               end
+               return false
+       end
+
+       private var p_tagname = "[A-Za-z][A-Za-z0-9-]*"
+       private var p_attribute_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
+       private var p_uquoted_value = "[^\"'=<>` \t\n]+"
+       private var p_squoted_value = "'[^']*'"
+       private var p_dquoted_value = "\"[^\"]*\""
+       private var p_attribute_value = "({p_uquoted_value}|{p_squoted_value}|{p_dquoted_value})"
+       private var p_attribute_value_spec = "(\\s*=\\s*{p_attribute_value})"
+       private var p_attribute = "(\\s{p_attribute_name}{p_attribute_value_spec}?)"
+       private var p_opentag = "<{p_tagname}{p_attribute}*\\s*/?>"
+       private var p_closetag = "</{p_tagname}\\s*[>]"
+       private var p_html_comment = "<!---->|<!--(-?[^>-])(-?[^-])*-->"
+       private var p_processing_instruction = "[<][?].*?[?][>]"
+       private var p_declaration = "<![A-Z]+\\s+[^>]*>"
+       private var p_cdata = "<!\\[CDATA\\[.*\\]\\]>"
+       private var p_html_tag = "({p_opentag}|{p_closetag}|{p_html_comment}|{p_processing_instruction}|{p_declaration}|{p_cdata})"
+
+       # HTML tag pattern
+       private var re_html_tag: Regex do
+               var re = "^{p_html_tag}".to_re
+               re.ignore_case = true
+               return re
+       end
+
+       # Attempt to parse an HTML entity
+       private fun parse_entity: Bool do
+               var m = match(re_entity_here)
+               if m != null then
+                       append_text(m)
+                       return true
+               end
+               return false
+       end
+
+       # HTML entity pattern
+       private var re_entity_here: Regex do
+               var re = "^&(#x[a-f0-9]\{1,8\}|#[0-9]\{1,8\}|[a-z][a-z0-9]\{1,31\});".to_re
+               re.ignore_case = true
+               return re
+       end
+
+       # Parse a run of ordinary characters
+       #
+       # Or a single character with a special meaning in markdown, as a plain string.
+       private fun parse_string: Bool do
+               var begin = index
+               var begin_column = column
+               var length = input.length
+               while index != length do
+                       if special_characters.has(input.chars[index]) then
+                               break
+                       end
+                       advance 1
+               end
+               if begin != index then
+                       var column_before = column
+                       column = begin_column
+                       append_text(input, begin, index)
+                       column = column_before
+                       return true
+               end
+               return false
+       end
+
+       # Scan a sequence of characters with code `delimiter_char`
+       #
+       # Return information about the number of delimiters and whether they are positioned
+       # such as they can open and/or close emphasis or strong emphasis.
+       private fun scan_delimiters(delimiter_processor: MdDelimiterProcessor, delimiter_char: Char): nullable MdDelimiterData do
+               var start_index = index
+               var start_column = column
+
+               var delimiter_count = 0
+               while peek == delimiter_char do
+                       delimiter_count += 1
+                       advance 1
+               end
+
+               if delimiter_count < delimiter_processor.min_length then
+                       index = start_index
+                       column = start_column
+                       return null
+               end
+
+               var before = "\n"
+               if start_index > 0 then
+                       before = input.substring(start_index - 1, 1)
+               end
+
+               var char_after = peek
+               var after = "\n"
+               if char_after != '\0' then
+                       after = char_after.to_s
+               end
+
+               var before_is_punctuation = before.has(re_punctuation)
+               var before_is_whitespace = before.has(re_whitespace_char)
+               var after_is_punctuation = after.has(re_punctuation)
+               var after_is_whitespace = after.has(re_whitespace_char)
+
+               var left_flanking = not after_is_whitespace and
+                       (not after_is_punctuation or before_is_whitespace or before_is_punctuation)
+               var right_flanking = not before_is_whitespace and
+                       (not before_is_punctuation or after_is_whitespace or after_is_punctuation)
+
+               var can_open
+               var can_close
+               if delimiter_char == '_' then
+                       can_open = left_flanking and (not right_flanking or before_is_punctuation)
+                       can_close = right_flanking and (not left_flanking or after_is_punctuation)
+               else
+                       can_open = left_flanking and delimiter_char == delimiter_processor.opening_delimiter
+                       can_close = right_flanking and delimiter_char == delimiter_processor.closing_delimiter
+               end
+
+               index = start_index
+               column = start_column
+               return new MdDelimiterData(delimiter_count, can_open, can_close)
+       end
+
+       # Punctuation pattern
+       private var re_punctuation: Regex = "^[]!\"#\\$%&'()*+,.:;<=>?@^_`\{|\}~[-]".to_re
+
+       # Whitespace character start pattern
+       private var re_whitespace_char: Regex = "^[  \t\r\n]".to_re
+
+       # Process the stack of delimiters
+       private fun process_delimiters(stack_bottom: nullable MdDelimiter) do
+               var openers_bottom = new HashMap[Char, nullable MdDelimiter]
+
+               # find first closer above stack bottom
+               var closer = last_delimiter
+               while closer != null and closer.prev != stack_bottom do
+                       closer = closer.prev
+               end
+               # move forward, looking for closers, and handling each
+               while closer != null do
+                       var delimiter_char = closer.delimiter_char
+
+                       if not closer.can_close then
+                               closer = closer.next
+                               continue
+                       end
+
+                       if not delimiter_processors_map.has_key(delimiter_char) then
+                               closer = closer.next
+                               continue
+                       end
+
+                       var delimiter_processor = delimiter_processors_map[delimiter_char]
+                       var opening_delimiter_char = delimiter_processor.opening_delimiter
+
+                       # Found delimiter closer. Now look back for first matching opener
+                       var use_delims = 0
+                       var opener_found = false
+                       var potential_opener_found = false
+                       var opener = closer.prev
+
+                       while opener != null and opener != stack_bottom and (not openers_bottom.has_key(delimiter_char) or opener != openers_bottom[delimiter_char]) do
+
+                               if opener.can_open and opener.delimiter_char == opening_delimiter_char then
+                                       potential_opener_found = true
+                                       use_delims = delimiter_processor.delimiter_use(opener, closer)
+                                       if use_delims > 0 then
+                                               opener_found = true
+                                               break
+                                       end
+                               end
+                               opener = opener.prev
+                       end
+
+                       if not opener_found then
+                               if not potential_opener_found then
+                                       # Set lower bound for future searches for openers.
+                                       # Only do this when we didn't even have a potential opener
+                                       # (one that matches the character and can open).
+                                       # If an opener was rejected because of the number of delimiters
+                                       # (e.g. because of the "multiple of 3" rule),
+                                       # we want to consider it next time because the number of delimiter
+                                       # can change as we continue processing.
+                                       openers_bottom[delimiter_char] = closer.prev
+                                       if not closer.can_open then
+                                               # We can remove a closer that can't be an opener,
+                                               # once we've seen there's no matching opener.
+                                               remove_delimiters_keep_node(closer)
+                                       end
+                               end
+                               closer = closer.next
+                               continue
+                       end
+
+                       var opener_node = opener.as(not null).node
+                       var closer_node = closer.node
+
+                       # Remove number of used delimieters from stack and inline nodes
+                       opener.as(not null).length -= use_delims
+                       closer.length -= use_delims
+                       opener_node.literal = opener_node.literal.substring(0,
+                               opener_node.literal.length - use_delims)
+                       closer_node.literal = closer_node.literal.substring(0,
+                               closer_node.literal.length - use_delims)
+
+                       remove_delimiters_between(opener, closer)
+                       # The delimieter processor can re-parent the nodes between opener and closer,
+                       # so make sure they're contiguous already.
+                       # Exclusive because we want to keep opener/closer themselves.
+                       merge_text_nodes_between_exclusive(opener_node, closer_node)
+                       delimiter_processor.process(opener_node, closer_node, use_delims)
+
+                       # Node delimieter characters left to process, so we can remove
+                       # delimieter and the now empty node
+                       if opener.as(not null).length == 0 then
+                               remove_delimiters_and_node(opener)
+                       end
+
+                       if closer.length == 0 then
+                               var next = closer.next
+                               remove_delimiters_and_node(closer)
+                               closer = next
+                       end
+               end
+
+               # Remove all delimiters
+               while last_delimiter != null and last_delimiter != stack_bottom do
+                       remove_delimiters_keep_node(last_delimiter)
+               end
+       end
+
+       # Remove all delimiters between `opener` and `closer`
+       private fun remove_delimiters_between(opener, closer: nullable MdDelimiter) do
+               if opener == null or closer == null then return
+
+               var delimiter = closer.prev
+               while delimiter != null and delimiter != opener do
+                       var previous_delimiter = delimiter.prev
+                       remove_delimiters_keep_node(delimiter)
+                       delimiter = previous_delimiter
+               end
+       end
+
+       # Remove the delimiter and the corresponding text node
+       #
+       # For used delimiters, e.g. `*` in `*foo*`.
+       private fun remove_delimiters_and_node(delim: nullable MdDelimiter) do
+               if delim == null then return
+
+               var node = delim.node
+               node.unlink
+               remove_delimiter(delim)
+       end
+
+       # Remove the delimiter but keep the corresponding node as text
+       #
+       # For unused delimiters such as `_` in `foo_bar`.
+       private fun remove_delimiters_keep_node(delim: nullable MdDelimiter) do
+               remove_delimiter(delim)
+       end
+
+       # Remove the delimiter `delim`
+       private fun remove_delimiter(delim: nullable MdDelimiter) do
+               if delim == null then return
+
+               var prev = delim.prev
+               if prev != null then
+                       prev.next = delim.next
+               end
+               var next = delim.next
+               if next == null then
+                       # top of stack
+                       last_delimiter = prev
+               else
+                       next.prev = prev
+               end
+       end
+
+       # Merge all nodes between `from` and `to` excluding `from` and `to`
+       private fun merge_text_nodes_between_exclusive(from, to: nullable MdNode) do
+               if from == null or to == null then return
+               # no node between them
+               if from == to or from.next == to then return
+               merge_text_nodes_inclusive(from.next, to.prev)
+       end
+
+       # Merge all child nodes of `node` into one
+       private fun merge_child_text_nodes(node: nullable MdNode) do
+               if node == null then return
+               # no children or just one child node, no need for merging
+               if node.first_child == node.last_child then return
+               merge_text_nodes_inclusive(node.first_child, node.last_child)
+       end
+
+       # Merge all nodes between `from` and `to` including `from` and `to`
+       private fun merge_text_nodes_inclusive(from, to: nullable MdNode) do
+               var first = null
+               var last = null
+
+               var node = from
+               while node != null do
+                       if node isa MdText then
+                               var text = node
+                               if first == null then first = text
+                               last = text
+                       else
+                               merge_if_needed(first, last)
+                               first = null
+                               last = null
+                       end
+                       if node == to then break
+                       node = node.next
+               end
+               merge_if_needed(first, last)
+       end
+
+       # Merge all nodes between `first` and `last`
+       private fun merge_if_needed(first, last: nullable MdText) do
+               if first != null and last != null and first != last then
+                       var buffer = new Buffer
+                       buffer.append(first.literal)
+                       var node = first.next
+                       var stop = last.next
+                       while node != null and node != stop do
+                               buffer.append(node.as(MdText).literal)
+                               first.location.line_end = node.location.line_end
+                               first.location.column_end = node.location.column_end
+                               var unlink = node
+                               node = node.next
+                               unlink.unlink
+                       end
+                       var literal = buffer.write_to_string
+                       first.literal = literal
+               end
+       end
+end
+
+# Custom delimiter processor for additional delimiters besides `_` and `*`
+interface MdDelimiterProcessor
+
+       # The character that marks the beginning of a delimited node
+       #
+       # Must not clash with anu built-in special characters.
+       fun opening_delimiter: Char is abstract
+
+       # The character that marks the ending of a delimited node
+       #
+       # Must not clash with anu built-in special characters.
+       fun closing_delimiter: Char is abstract
+
+       # Minimum number of delimiters characters that are needed to active this
+       #
+       # Must be at least one.
+       fun min_length: Int is abstract
+
+       # Determine how many (if any) of the delimiter characters should be used
+       #
+       # This allows implementations to decide how many characters to use based on the
+       # properties of the delimiter runs.
+       #
+       # An implementation can also return 0 when it doesn't want to allow this particular
+       # combination of delimiter runs.
+       fun delimiter_use(opener, closer: MdDelimiter): Int is abstract
+
+       # Process the matched delimiters
+       #
+       # For example, by wrapping the nodes between `opener` and `closer` in a new node,
+       # or appending a new node after the opener.
+       #
+       # Note that removal of the delimiter from the delimiter nodes and unlinking
+       # them is done by the caller.
+       fun process(opener, closer: MdText, delimiter_use: Int) is abstract
+end
+
+# A delimiter is one or more of the same delimiter character
+#
+# Used for paired delimiters like emphasis or strong emphasis.
+class MdDelimiter
+
+       # Node containing the delimiter
+       var node: MdText
+
+       # Character used as delimiter
+       var delimiter_char: Char
+
+       # Can `self` open a delimiter?
+       var can_open: Bool
+
+       # Cant `self` close a delimiter?
+       var can_close: Bool
+
+       # Previous delimiter found
+       var prev: nullable MdDelimiter
+
+       # Next delimiter found
+       var next: nullable MdDelimiter
+
+       # The number of characters in this delimiter run that are left for processing
+       var length = 1
+
+       # The number of characters originally in this delimiter run
+       #
+       # At the start of processing, this is the same as `length`.
+       var original_length = 1
+end
+
+# Opening bracket for links and images
+class MdBracket
+
+       # Node containing the bracket
+       var node: MdText
+
+       # Index of the bracket in the original string
+       var index: Int
+
+       # COlumn of the bracket
+       var column: Int
+
+       # Is this bracket opening an image?
+       var is_image: Bool
+
+       # Previous bracket
+       var prev: nullable MdBracket
+
+       # Previous delimiter
+       var prev_delimiter: nullable MdDelimiter
+
+       # Whether this bracket is allowed to form a link/image
+       var allowed = true
+
+       # Whether there is an unescaped bracket (opening or closing) anywhere after this bracket
+       var bracket_after = false
+
+       # Create a new bracket for a link
+       init link(node: MdText, index: Int, column: Int, prev: nullable MdBracket, prev_delimiter: nullable MdDelimiter) do
+               init(node, index, column, false, prev, prev_delimiter)
+       end
+
+       # Create a new bracket for an image
+       init image(node: MdText, index: Int, column: Int, prev: nullable MdBracket, prev_delimiter: nullable MdDelimiter) do
+               init(node, index, column, true, prev, prev_delimiter)
+       end
+end
+
+# Data about a delimiter parsing
+private class MdDelimiterData
+
+       # Number of successive delimiters found
+       var count: Int
+
+       # Can this delimiter open an inline construct?
+       var can_open: Bool
+
+       # Can this delimiter close an inline construct?
+       var can_close: Bool
+end
+
+# An implementation of MdDelimiterProcessor that dispatches all calls to others
+#
+# The sub processors called bepends on the length of the delimiter run.
+# All child processors must have different minimum lengths.
+# A given delimiter run is dispatched to the child with the largest acceptable minimum length.
+# If not child is applicable, the one with the largest minimum length is chosen.
+class MdStaggeredDelimiterProcessor
+       super MdDelimiterProcessor
+
+       # Delimiter character
+       var delim: Char
+
+       # Sub processors to apply
+       var processors = new Array[MdDelimiterProcessor]
+
+       redef var min_length = 0
+       redef fun opening_delimiter do return delim
+       redef fun closing_delimiter do return delim
+
+       # Add a new sub delimiter processor
+       fun add(dp: MdDelimiterProcessor) do
+               var len = dp.min_length
+               var i = 0
+               while i < processors.length do
+                       var p = processors[i]
+                       assert len != p.min_length else
+                               print "Cannot add two delimiter processor for `{delim}` " +
+                                       "and mininimum length `{len}`"
+                       end
+                       if len > p.min_length then
+                               break
+                       end
+                       i += 1
+               end
+               processors.insert(dp, i)
+       end
+
+       # Find the corresponding processor for a length of `len` delimiter characters
+       fun find_processor(len: Int): MdDelimiterProcessor do
+               for processor in processors do
+                       if processor.min_length <= len then return processor
+               end
+               return processors.first
+       end
+
+       redef fun delimiter_use(opener, closer) do
+               return find_processor(opener.length).delimiter_use(opener, closer)
+       end
+
+       redef fun process(opener, closer, delimiter_use) do
+               find_processor(delimiter_use).process(opener, closer, delimiter_use)
+       end
+end
+
+# A processor for emphasis tokens
+class MdEmphasisDelimiterProcessor
+       super MdDelimiterProcessor
+
+       # Delimiter character
+       var delimiter_char: Char
+
+       redef var min_length = 1
+       redef fun opening_delimiter do return delimiter_char
+       redef fun closing_delimiter do return delimiter_char
+
+       redef fun delimiter_use(opener, closer) do
+               # "multiple of 3" rule for internal delimiter runs
+               if (opener.can_close or closer.can_open) and
+                  ((opener.original_length + closer.original_length) % 3 == 0) then
+                       return 0
+               end
+               # calculate actual number of delimiters used from this closer
+               if opener.length >= 2 and closer.length >= 2 then
+                       return 2
+               end
+               return 1
+       end
+
+       redef fun process(opener, closer, delimiter_use) do
+               var single_delimiter = opening_delimiter.to_s
+               var emphasis: MdNode
+               if delimiter_use == 1 then
+                       emphasis = new MdEmphasis(
+                               new MdLocation(
+                                       opener.location.line_start,
+                                       opener.location.column_start,
+                                       closer.location.line_end,
+                                       closer.location.column_end),
+                               single_delimiter)
+               else
+                       emphasis = new MdStrongEmphasis(
+                               new MdLocation(
+                                       opener.location.line_start,
+                                       opener.location.column_start + opener.literal.length,
+                                       closer.location.line_end,
+                                       closer.location.column_end - closer.literal.length),
+                               "{single_delimiter}{single_delimiter}")
+               end
+               var tmp = opener.next
+               while tmp != null and tmp != closer do
+                       var next = tmp.next
+                       emphasis.append_child(tmp)
+                       tmp = next
+               end
+               opener.insert_after(emphasis)
+       end
+end
+
+# Asterisk delimiters processor
+class MdAsteriskDelimiterProcessor
+       super MdEmphasisDelimiterProcessor
+       noautoinit
+
+       redef var delimiter_char = '*'
+end
+
+# Underscore delimters processor
+class MdUnderscoreDelimiterProcessor
+       super MdEmphasisDelimiterProcessor
+       noautoinit
+
+       redef var delimiter_char = '_'
+end
+
+# Utils
+
+redef class String
+
+       # Remove escape backslash from string
+       fun unescape_string: String do
+               if not has(re_escaped) then return self
+
+               var buffer = new Buffer
+               var match = search(re_escaped)
+               var last_end = 0
+               while match != null do
+                       buffer.append substring(last_end, match.from - last_end)
+                       buffer.append substring(match.from + 1, 1)
+                       last_end = match.after
+                       match = search_from(re_escaped, last_end)
+               end
+               if last_end < length then
+                       buffer.append substring(last_end, length - last_end)
+               end
+               return buffer.to_s
+       end
+
+       # Normalize link reference names
+       private fun normalize_reference: String do
+               var stripped = self.substring(1, length - 2).trim
+               var lowercase = stripped.to_lower # TODO utf-8
+               return lowercase.replace(re_whitespace, " ")
+       end
+end
+
+redef class Sys
+       private var p_escapable = "[]!\"#$%&\'()*+,./:;<=>?@\\[\\\\^_`\\\{|\\\}~-]"
+       private var re_escaped: Regex = "\\\\{p_escapable}".to_re
+       private var re_whitespace: Regex = "\\s+".to_re
+end
diff --git a/lib/markdown2/markdown_latex_rendering.nit b/lib/markdown2/markdown_latex_rendering.nit
new file mode 100644 (file)
index 0000000..b7c4abd
--- /dev/null
@@ -0,0 +1,438 @@
+# 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.
+
+# LaTeX rendering of Markdown documents
+module markdown_latex_rendering
+
+import markdown_rendering
+import markdown_github
+import markdown_wikilinks
+
+# Markdown document renderer to LaTeX
+class LatexRenderer
+       super MdRenderer
+
+       # Generate the LaTeX document wrapper
+       #
+       # The header includes:
+       #  * document class
+       #  * package importation
+       #  * begin and end document directives
+       var wrap_document = false is optional, writable
+
+       # LaTeX document class
+       #
+       # Default is `article`.
+       var document_class = "article" is optional, writable
+
+       # LaTeX document page format
+       #
+       # Default is `letter`.
+       var page_format = "letter" is optional, writable
+
+       # LaTeX font size
+       #
+       # Default is `10pt`.
+       var font_size = "10pt" is optional, writable
+
+       # Use `listings` package for code blocks?
+       var use_listings = false is optional, writable
+
+       # LaTeX output under construction
+       private var latex: Buffer is noinit
+
+       # Render `document` as LaTeX
+       redef fun render(document) do
+               latex = new Buffer
+               enter_visit(document)
+               return latex.write_to_string
+       end
+
+       redef fun visit(node) do node.render_latex(self)
+
+       # Indentation level
+       var indent = 0
+
+       # Add a raw `string` to the output
+       #
+       # Raw means that the string will not be escaped.
+       fun add_raw(string: String) do latex.append string
+
+       # Add `text` string to the output
+       #
+       # The string will be escaped.
+       fun add_text(text: String) do latex.append latex_escape(text)
+
+       # Add a blank line to the output
+       fun add_line do
+               if not latex.is_empty and latex.last != '\n' then
+                       latex.add '\n'
+               end
+       end
+
+       # Add an indentation depending on `ident` level
+       fun add_indent do latex.append " " * indent
+
+       # Escape `string` to LaTeX
+       fun latex_escape(string: String): String do
+               var buffer = new Buffer
+               for i in [0 .. string.length[ do
+                       var c = string.chars[i]
+                       if c == '>' then
+                               buffer.append "\\textgreater"
+                               continue
+                       else if c == '<' then
+                               buffer.append "\\textless"
+                               continue
+                       else if c == '\\' then
+                               buffer.append "\\textbackslash"
+                               continue
+                       else if escaped_chars.has(c) then
+                               buffer.add '\\'
+                       end
+                       buffer.add c
+               end
+               return buffer.to_s
+       end
+
+       # LaTeX characters to escape
+       var escaped_chars = ['%', '$', '{', '}', '_', '#', '&']
+end
+
+redef class MdNode
+
+       # Render `self` as HTML
+       fun render_latex(v: LatexRenderer) do visit_all(v)
+end
+
+# Blocks
+
+redef class MdDocument
+       redef fun render_latex(v) do
+               var wrap_document = v.wrap_document
+               if v.wrap_document then
+                       v.add_line
+                       v.add_raw "\\documentclass[{v.page_format},{v.font_size}]\{{v.document_class}\}\n\n"
+                       v.add_raw "\\usepackage[utf8]\{inputenc\}\n"
+                       if v.use_listings then
+                               v.add_raw "\\usepackage\{listings\}\n"
+                       end
+                       v.add_raw "\\usepackage\{hyperref\}\n"
+                       v.add_raw "\\usepackage\{graphicx\}\n"
+                       v.add_raw "\\usepackage\{ulem\}\n\n"
+                       v.add_raw "\\begin\{document\}\n\n"
+               end
+               var node = first_child
+               while node != null do
+                       v.enter_visit node
+                       node = node.next
+                       if node != null then v.add_raw "\n"
+               end
+               if wrap_document then
+                       v.add_raw "\n\\end\{document\}\n"
+               end
+       end
+end
+
+redef class MdHeading
+       redef fun render_latex(v) do
+               var level = self.level
+               v.add_indent
+               v.add_line
+               if level == 1 then
+                       v.add_raw "\\section\{"
+               else if level == 2 then
+                       v.add_raw "\\subsection\{"
+               else if level == 3 then
+                       v.add_raw "\\subsubsection\{"
+               else if level == 4 then
+                       v.add_raw "\\paragraph\{"
+               else if level == 5 then
+                       v.add_raw "\\subparagraph\{"
+               else
+                       # use bold for level 6 headings
+                       v.add_raw "\\textbf\{"
+               end
+               v.add_indent
+               visit_all(v)
+               v.add_raw "\}"
+               v.add_line
+       end
+end
+
+redef class MdBlockQuote
+       redef fun render_latex(v) do
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{quote\}"
+               v.add_line
+               v.indent += 2
+               visit_all(v)
+               v.indent -= 2
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{quote\}"
+               v.add_line
+       end
+end
+
+redef class MdIndentedCodeBlock
+       redef fun render_latex(v) do
+               var directive = if v.use_listings then "lstlisting" else "verbatim"
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{{directive}\}"
+               v.add_line
+               v.add_raw literal or else ""
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{{directive}\}"
+               v.add_line
+       end
+end
+
+redef class MdFencedCodeBlock
+       redef fun render_latex(v) do
+               var info = self.info
+               var lstlistings = v.use_listings
+               var directive = if lstlistings then "lstlisting" else "verbatim"
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{{directive}\}"
+               if lstlistings and info != null and not info.is_empty then
+                       v.add_raw "[language={info}]"
+               end
+               v.add_line
+               v.add_raw literal or else ""
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{{directive}\}"
+               v.add_line
+       end
+end
+
+redef class MdOrderedList
+       redef fun render_latex(v) do
+               var start = self.start_number
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{enumerate\}"
+               v.indent += 2
+               v.add_line
+               if start != 1 then
+                       v.add_indent
+                       v.add_raw "\\setcounter\{enum{nesting_level}\}\{{start}\}"
+                       v.add_line
+               end
+               visit_all(v)
+               v.indent -= 2
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{enumerate\}"
+               v.add_line
+       end
+
+       # Depth of ordered list
+       #
+       # Used to compute the `setcounter` level.
+       fun nesting_level: String do
+               var nesting = 1
+
+               var parent = self.parent
+               while parent != null do
+                       if parent isa MdOrderedList then nesting += 1
+                       parent = parent.parent
+               end
+
+               if nesting <= 3 then
+                       return "i" * nesting
+               end
+               return "iv"
+       end
+end
+
+redef class MdUnorderedList
+       redef fun render_latex(v) do
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{itemize\}"
+               v.add_line
+               v.indent += 2
+               visit_all(v)
+               v.indent -= 2
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{itemize\}"
+               v.add_line
+       end
+end
+
+redef class MdListItem
+       redef fun render_latex(v) do
+               v.add_indent
+               v.add_raw "\\item"
+               v.add_line
+               v.indent += 2
+               visit_all(v)
+               v.indent -= 2
+               v.add_line
+       end
+end
+
+redef class MdThematicBreak
+       redef fun render_latex(v) do
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{center\}\\rule\{3in\}\{0.4pt\}\\end\{center\}"
+               v.add_line
+       end
+end
+
+redef class MdParagraph
+       redef fun render_latex(v) do
+               v.add_indent
+               visit_all(v)
+               v.add_line
+       end
+end
+
+
+redef class MdHtmlBlock
+       redef fun render_latex(v) do
+               v.add_line
+               v.add_indent
+               v.add_raw "\\begin\{verbatim\}"
+               v.add_line
+               v.add_indent
+               v.add_raw literal or else ""
+               v.add_line
+               v.add_indent
+               v.add_raw "\\end\{verbatim\}"
+               v.add_line
+       end
+end
+
+# Inlines
+
+redef class MdLineBreak
+       redef fun render_latex(v) do
+               v.add_line
+               v.add_indent
+       end
+end
+
+redef class MdCode
+       redef fun render_latex(v) do
+               v.add_raw "\\texttt\{"
+               v.add_text literal
+               v.add_raw "\}"
+       end
+end
+
+redef class MdEmphasis
+       redef fun render_latex(v) do
+               v.add_raw "\\textit\{"
+               visit_all(v)
+               v.add_raw "\}"
+       end
+end
+
+redef class MdStrongEmphasis
+       redef fun render_latex(v) do
+               v.add_raw "\\textbf\{"
+               visit_all(v)
+               v.add_raw "\}"
+       end
+end
+
+redef class MdHtmlInline
+       redef fun render_latex(v) do
+               v.add_raw "\\texttt\{"
+               v.add_raw v.latex_escape(literal)
+               v.add_raw "\}"
+       end
+end
+
+redef class MdImage
+       redef fun render_latex(v) do
+               v.add_raw "\\includegraphics\{"
+               v.add_text destination
+               v.add_raw "\}"
+       end
+
+       private fun alt_text: String do
+               var v = new RawTextVisitor
+               return v.render(self)
+       end
+end
+
+redef class MdLink
+       redef fun render_latex(v) do
+               if is_autolink then
+                       v.add_raw "\\url\{"
+                       v.add_text destination
+                       v.add_raw "\}"
+                       return
+               end
+               var title = self.title
+               v.add_raw "\\href\{"
+               v.add_text destination
+               v.add_raw "\}\{"
+               visit_all(v)
+               if title != null and not title.is_empty then
+                       v.add_raw " ("
+                       v.add_text title
+                       v.add_raw ")"
+               end
+               v.add_raw "\}"
+       end
+end
+
+redef class MdText
+       redef fun render_latex(v) do
+               v.add_text literal
+       end
+end
+
+# Github mode
+
+redef class MdStrike
+       redef fun render_latex(v) do
+               v.add_raw "\\sout\{"
+               visit_all(v)
+               v.add_raw "\}"
+       end
+end
+
+redef class MdSuper
+       redef fun render_latex(v) do
+               v.add_raw "\\textsuperscript\{"
+               visit_all(v)
+               v.add_raw "\}"
+       end
+end
+
+# Wikilinks
+
+redef class MdWikilink
+       redef fun render_latex(v) do
+               v.add_raw "\\texttt\{"
+               var title = self.title
+               if title != null then
+                       v.add_text "{title} | "
+               end
+               v.add_text link
+               v.add_raw "\}"
+       end
+end
diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit
new file mode 100644 (file)
index 0000000..1e047d6
--- /dev/null
@@ -0,0 +1,258 @@
+# 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.
+
+# Manpages rendering of Markdown documents
+module markdown_man_rendering
+
+import markdown_rendering
+import markdown_github
+import markdown_wikilinks
+
+# Markdown document renderer to Manpage
+class ManRenderer
+       super MdRenderer
+
+       # Output under construction
+       private var man: Buffer is noinit
+
+       # Render `node` as Markdown
+       redef fun render(node) do
+               man = new Buffer
+               enter_visit(node)
+               return man.write_to_string
+       end
+
+       redef fun visit(node) do node.render_man(self)
+
+       # Add `string` to `man`
+       fun add(string: String) do
+               man.append(string.replace("-", "\\-"))
+       end
+
+       # Add code that need to be escaped
+       fun add_code(code: String) do
+               add code.replace(" ", "\\ ")
+       end
+
+       # Add a blank line to the output
+       fun add_line do
+               add "\n"
+       end
+end
+
+redef class MdNode
+
+       # Render `self` as Manpage format
+       fun render_man(v: ManRenderer) do visit_all(v)
+end
+
+# Blocks
+
+redef class MdBlockQuote
+       redef fun render_man(v) do
+               v.add ".RS"
+               visit_all(v)
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdCodeBlock
+       redef fun render_man(v) do
+               v.add ".RS\n.nf\n\\f[C]"
+               v.add_line
+
+               var literal = self.literal
+               if literal != null then
+                       var lines = literal.split("\n")
+                       for i in [0 .. lines.length[ do
+                               if i == lines.length - 1 then break
+                               var line = lines[i]
+                               v.add_code line
+                               v.add_line
+                       end
+               end
+
+               v.add "\\f[]\n.fi\n.RE"
+               v.add_line
+       end
+end
+
+redef class MdHeading
+       redef fun render_man(v) do
+               var level = self.level
+
+               if level == 1 then
+                       v.add ".SH "
+               else if level == 2 then
+                       v.add ".SS "
+               else if level >= 3 then
+                       # We use dictionary (titled paragraph) to simulate a 3rd level (or more)
+                       v.add ".TP\n"
+               end
+               visit_all(v)
+               v.add_line
+       end
+end
+
+redef class MdUnorderedList
+       redef fun render_man(v) do
+               v.add ".RS"
+               v.add_line
+
+               var node = first_child
+               while node != null do
+                       v.add ".IP \\[bu] 3"
+                       v.add_line
+                       v.enter_visit node
+                       v.add_line
+                       node = node.next
+               end
+
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdOrderedList
+       redef fun render_man(v) do
+               v.add ".RS"
+               v.add_line
+
+               var index = start_number
+               var node = first_child
+               while node != null do
+                       v.add ".IP \"{index}.\" 3"
+                       v.add_line
+                       v.enter_visit node
+                       v.add_line
+                       node = node.next
+                       index += 1
+               end
+
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdParagraph
+       redef fun render_man(v) do
+               var in_list = is_in_list
+               if not in_list then
+                       v.add_line
+               end
+               visit_all(v)
+               if not in_list then
+                       v.add_line
+               end
+       end
+end
+
+redef class MdThematicBreak
+       redef fun render_man(v) do
+               v.add "***"
+               v.add_line
+       end
+end
+
+redef class MdHtmlBlock
+       redef fun render_man(v) do
+               v.add_line
+               v.add literal or else ""
+               v.add_line
+       end
+end
+
+# Inlines
+
+redef class MdLineBreak
+       redef fun render_man(v) do
+               v.add_line
+       end
+end
+
+redef class MdCode
+       redef fun render_man(v) do
+               v.add "\\f[C]"
+               v.add_code literal
+               v.add "\\f[]"
+       end
+end
+
+redef class MdEmphasis
+       redef fun render_man(v) do
+               v.add "\\f[I]"
+               visit_all(v)
+               v.add "\\f[]"
+       end
+end
+
+redef class MdStrongEmphasis
+       redef fun render_man(v) do
+               v.add "\\f[B]"
+               visit_all(v)
+               v.add "\\f[]"
+       end
+end
+
+redef class MdHtmlInline
+       redef fun render_man(v) do
+               v.add literal
+       end
+end
+
+redef class MdLinkOrImage
+       redef fun render_man(v) do
+               var title = self.title
+
+               visit_all(v)
+               v.add " ("
+               v.add destination
+               if title != null and not title.is_empty then
+                       v.add " "
+                       v.add title
+               end
+               v.add ")"
+       end
+end
+
+redef class MdText
+       redef fun render_man(v) do
+               v.add literal
+       end
+end
+
+# Github
+
+redef class MdStrike
+       redef fun render_man(v) do
+               v.add "[STRIKEOUT:"
+               visit_all(v)
+               v.add "]"
+       end
+end
+
+# Wikilinks
+
+redef class MdWikilink
+       redef fun render_man(v) do
+               v.add "("
+               var title = self.title
+               if title != null then
+                       v.add "{title} | "
+               end
+               v.add link
+               v.add ")"
+       end
+end
diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit
new file mode 100644 (file)
index 0000000..eadda1b
--- /dev/null
@@ -0,0 +1,392 @@
+# 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
+import markdown_github
+import markdown_wikilinks
+
+# 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
+
+# Wikilinks
+
+redef class MdWikilink
+       redef fun render_md(v) do
+               v.add_raw "[["
+               var title = self.title
+               if title != null then
+                       v.add_raw "{title} | "
+               end
+               v.add_raw link
+               v.add_raw "]]"
+       end
+end
diff --git a/lib/markdown2/markdown_rendering.nit b/lib/markdown2/markdown_rendering.nit
new file mode 100644 (file)
index 0000000..ff49d1c
--- /dev/null
@@ -0,0 +1,69 @@
+# 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 document rendering
+module markdown_rendering
+
+import markdown_ast
+
+# Common interface for all markdown renderer
+interface MdRenderer
+       super MdVisitor
+
+       # Render `node`
+       fun render(node: MdNode): String is abstract
+end
+
+# A renderer that output raw text
+class RawTextVisitor
+       super MdRenderer
+
+       # Text under construction
+       private var text: Buffer is noinit
+
+       redef fun render(node) do
+               text = new Buffer
+               enter_visit(node)
+               return text.to_s
+       end
+
+       # Append `string` to `text`
+       fun add(string: String) do text.append(string)
+
+       redef fun visit(node) do node.render_raw_text(self)
+end
+
+redef class MdNode
+
+       # Return `self` as raw text
+       fun raw_text: String do
+               var v = new RawTextVisitor
+               return v.render(self)
+       end
+
+       # Render `self` as raw text
+       fun render_raw_text(v: RawTextVisitor) do visit_all(v)
+end
+
+redef class MdCode
+       redef fun render_raw_text(v) do v.add literal
+end
+
+redef class MdLineBreak
+       redef fun render_raw_text(v) do v.add "\n"
+end
+
+redef class MdText
+       redef fun render_raw_text(v) do v.add literal
+end
diff --git a/lib/markdown2/markdown_wikilinks.nit b/lib/markdown2/markdown_wikilinks.nit
new file mode 100644 (file)
index 0000000..d6cc3a0
--- /dev/null
@@ -0,0 +1,119 @@
+# 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 wikilinks processing
+#
+# Enables parsing of `[[wikilinks]]` syntax.
+module markdown_wikilinks
+
+intrude import markdown_inline_parsing
+intrude import markdown_block_parsing
+
+redef class MdParser
+
+       # Enable wikilinks mode
+       var wikilinks_mode = false is writable
+
+       redef var inline_parser is lazy do
+               var parser = super
+               parser.wikilinks_mode = wikilinks_mode
+               return parser
+       end
+end
+
+redef class MdInlineParser
+
+       # Enable wikilinks mode
+       private var wikilinks_mode = false
+
+       redef fun parse_wikilink do
+               if not wikilinks_mode then return false
+
+               # do we have two opening bracket?
+               var last_bracket = self.last_bracket
+               if last_bracket == null then return false
+               var first_bracket = last_bracket.prev
+               if first_bracket == null then return false
+
+               # was the first bracket an image?
+               if first_bracket.is_image then return false
+
+               # do we have two closing brackets?
+               if index >= input.length or input.chars[index] != ']' then return false
+
+               advance 1 # skip last bracket
+               var start_index = first_bracket.index + 2
+               var end_index = index - 2
+
+               # create wikilink node
+               var content = input.substring(start_index, end_index - start_index)
+               var parts = content.split("|")
+               var title = if parts.length > 1 then parts.first.trim else null
+               var link = parts.last.trim
+
+               var wikilink = new MdWikilink(
+                               new MdLocation(
+                                       first_bracket.node.location.line_start,
+                                       first_bracket.node.location.column_start - 1,
+                                       line,
+                                       column - 1),
+                       link, title)
+
+               var node = last_bracket.node.next
+               var in_link = false
+               while node != null do
+                       var next = node.next
+                       if not in_link then
+                               if node isa MdText and node.literal.has("|") then
+                                       var buf = new Buffer
+                                       for c in node.literal.chars do
+                                               if c == '|' then
+                                                       in_link = true
+                                                       break
+                                               end
+                                               buf.add c
+                                       end
+                                       node.literal = buf.write_to_string.r_trim
+                               end
+                               wikilink.append_child(node)
+                       else
+                               node.unlink
+                       end
+                       node = next
+               end
+
+               append_node(wikilink)
+
+               # Process delimiters such as emphasis inside a link/image
+               process_delimiters(last_bracket.prev_delimiter)
+               merge_child_text_nodes(wikilink)
+
+               # remove brackets
+               first_bracket.node.unlink
+               last_bracket.node.unlink
+
+               return true
+       end
+end
+
+# A Wikilink node
+class MdWikilink
+       super MdNode
+
+       # Wikilink link
+       var link: String is writable
+
+       # Wikilink title
+       var title: nullable String = null is optional, writable
+end
diff --git a/lib/markdown2/nitmd.nit b/lib/markdown2/nitmd.nit
new file mode 100644 (file)
index 0000000..5a6970f
--- /dev/null
@@ -0,0 +1,73 @@
+# 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.
+
+# A Markdown parser for Nit.
+module nitmd
+
+import markdown_html_rendering
+import markdown_md_rendering
+import markdown_man_rendering
+import markdown_latex_rendering
+
+import config
+
+var opt_to = new OptionString("Specify output format (html, md, man, latex)", "-t", "--to")
+
+var usage = new Buffer
+usage.append "Usage: nitmd [-t format] [file.md]\n"
+usage.append "Translate Markdown documents to other formats.\n\n"
+usage.append "If no argument, read the Markdown input from `stdin`."
+
+var config = new Config
+config.add_option(opt_to)
+config.tool_description = usage.write_to_string
+
+config.parse_options(args)
+if config.args.length > 1 then
+       config.usage
+       exit 1
+end
+
+var md
+if config.args.is_empty then
+       md = sys.stdin.read_all
+else
+       var file = config.args.first
+       if not file.file_exists then
+               print "'{file}' not found"
+               exit 1
+       end
+       md = file.to_path.read_all
+end
+
+# Parse the input
+var parser = new MdParser
+var node = parser.parse(md)
+
+var renderer: MdRenderer
+var to = opt_to.value
+if to == null or to == "html" then
+       renderer = new HtmlRenderer
+else if to == "md" then
+       renderer = new MarkdownRenderer
+else if to == "man" then
+       renderer = new ManRenderer
+else if to == "latex" then
+       renderer = new LatexRenderer
+else
+       print "Unknown output format: {to}"
+       exit 1
+       return
+end
+printn renderer.render(node)
diff --git a/lib/markdown2/package.ini b/lib/markdown2/package.ini
new file mode 100644 (file)
index 0000000..a138d93
--- /dev/null
@@ -0,0 +1,12 @@
+[package]
+name=markdown2
+tags=format,lib
+maintainer=Alexandre Terrasa <alexandre@moz-code.org>
+license=Apache-2.0
+desc=A markdown parser for Nit
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/markdown2/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/markdown2/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/markdown2/tests/commonmark_gen.nit b/lib/markdown2/tests/commonmark_gen.nit
new file mode 100644 (file)
index 0000000..efebd80
--- /dev/null
@@ -0,0 +1,251 @@
+# 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.
+
+# Generate Nitunit tests from commonmark specification.
+#
+# See the full specification and the test cases at <http://commonmark.org/>.
+#
+# Usage:
+#
+# ~~~sh
+# commonmark_gen <commonmark_tests.json> <output directory>
+# ~~~
+module commonmark_gen
+
+import json
+import json::static
+import template
+
+# Generate the test cases from the JSON testfile.
+class TestGenerator
+
+       # Input file in containing the tests in JSON format
+       var json_file: String
+
+       # Output directory where the Nitunit files will be generated
+       var output_dir: String
+
+       # Ignored tests
+       #
+       # We ignore some tests for two reasons:
+       # * because `nitmd` does not fully support UTF-8
+       # * because somes tests are wrong
+       var ignored_tests: Array[Int] do
+               var ignored = new Array[Int]
+               ignored.add_all([171, 304, 305, 306, 311, 312, 313, 477, 514]) # utf-8 tests
+               ignored.add_all([275, 276]) # spec is wrong compared to reference implementation
+               return ignored
+       end
+
+       # Load the tests files from the JSON input
+       fun load_tests: Map[String, Array[TestCase]] do
+               var json = json_file.to_path.read_all.parse_json
+               var tests = new HashMap[String, Array[TestCase]]
+
+               for obj in json.as(JsonArray) do
+                       if not obj isa JsonObject then continue
+
+                       var number = obj["example"].as(Int)
+                       if ignored_tests.has(number) then continue
+
+                       var name = "test{number}"
+
+                       var section = obj["section"].as(String)
+                       if not tests.has_key(section) then
+                               tests[section] = new Array[TestCase]
+                       end
+
+                       var markdown = obj["markdown"].as(String)
+                       markdown = markdown.replace("\\", "\\\\")
+                       markdown = markdown.replace("\n", "\\n")
+                       markdown = markdown.replace("\t", "\\t")
+
+                       # fix missing chars in some tests
+                       if number == 162 then
+                               markdown = markdown.replace("my url", "my%20url")
+                       else if number == 467 then
+                               markdown = markdown.replace("my uri", "my%20uri")
+                       end
+
+                       var html = obj["html"].as(String)
+                       html = html.replace("\\", "\\\\")
+                       html = html.replace("\n", "\\n")
+                       html = html.replace("\t", "\\t")
+
+                       tests[section].add(new TestCase(name, markdown, html))
+               end
+
+               return tests
+       end
+
+       # Generate the Nitunit test files
+       fun gen_tests do
+               var tests = load_tests
+
+               for section, test_cases in tests do
+                       var test_file = new TestFile("test_commonmark_{strip_module_name(section)}")
+                       var test_class = new TestClass("TestCommonmark{strip_class_name(section)}")
+                       test_class.test_cases.add_all test_cases
+                       test_file.test_classes.add test_class
+                       test_file.save(output_dir)
+               end
+       end
+
+       # Strip module name
+       #
+       # Used to create a Nitunit module name from a CommonMark section title.
+       fun strip_module_name(name: String): String do
+               var b = new FlatBuffer
+               for c in name do
+                       if c == ' ' then
+                               b.add '_'
+                       else
+                               if not c.is_letter and
+                                  not c.is_digit and
+                                  not allowed_id_chars.has(c) then continue
+                               b.add c.to_lower
+                       end
+               end
+               return b.to_s
+       end
+
+       # Strip class name
+       #
+       # Used to create a Nitunit test class name from a CommonMark section title.
+       fun strip_class_name(name: String): String do
+               var b = new FlatBuffer
+               var was_space = false
+               for c in name do
+                       if c == ' ' then
+                               was_space = true
+                       else
+                               if not c.is_letter and
+                                  not c.is_digit and
+                                  not allowed_id_chars.has(c) then continue
+                               if was_space then
+                                       b.add c.to_upper
+                                       was_space = false
+                               else
+                                       b.add c
+                               end
+                       end
+               end
+               return b.to_s
+       end
+
+       private var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
+end
+
+# A Nitunit test file
+class TestFile
+
+       # Test module name
+       var test_file_name: String
+
+       # Test classes in this module
+       var test_classes = new Array[TestClass]
+
+       # Copyright header and module declaration
+       fun header: String do
+               return """
+# 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.
+
+module {{{test_file_name}}} is test
+
+import test_markdown
+"""
+       end
+
+       # Render the test module as a Nit string
+       fun render: String do
+               var tpl = new Template
+               tpl.add header
+               for test_class in test_classes do
+                       tpl.add test_class.render
+               end
+               return tpl.write_to_string
+       end
+
+       # Save the test module in `directory
+       fun save(directory: String) do
+               render.write_to_file(directory / "{test_file_name}.nit")
+       end
+end
+
+# A Nitunit test class
+class TestClass
+
+       # Test class name
+       var test_class_name: String
+
+       # Test cases in this test class
+       var test_cases = new Array[TestCase]
+
+       # Render the test class as a Nit string
+       fun render: String do
+               var tpl = new Template
+               tpl.addn "\nclass {test_class_name}"
+               tpl.addn "\tsuper TestMarkdownHtml"
+               tpl.addn "\ttest"
+               for test_case in test_cases do
+                       tpl.add test_case.render
+               end
+               tpl.addn "end"
+               return tpl.write_to_string
+       end
+end
+
+# A Nitunit test case
+class TestCase
+
+       # Test method name
+       var test_name: String
+
+       # Markdown input
+       var markdown: String
+
+       # Expected html output
+       var html: String
+
+       # Render the test case as a Nit string
+       fun render: String do
+               var tpl = new Template
+               tpl.addn "\n\tfun {test_name} is test do"
+               tpl.addn "\t\tvar md = \"\"\"{markdown}\"\"\""
+               tpl.addn "\t\tvar html = \"\"\"{html}\"\"\""
+               tpl.addn "\t\tassert md_to_html(md) == html"
+               tpl.addn "\tend"
+               return tpl.write_to_string
+       end
+end
+
+if args.length != 2 then
+       print "Usage: commonmark_gen <tests.json> <output_dir>"
+       exit 1
+end
+
+var gen = new TestGenerator(args.first, args.last)
+gen.gen_tests
diff --git a/lib/markdown2/tests/test_commonmark_atx_headings.nit b/lib/markdown2/tests/test_commonmark_atx_headings.nit
new file mode 100644 (file)
index 0000000..749ab18
--- /dev/null
@@ -0,0 +1,130 @@
+# 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.
+
+module test_commonmark_atx_headings is test
+
+import test_markdown
+
+class TestCommonmarkATXHeadings
+       super TestMarkdownHtml
+       test
+
+       fun test32 is test do
+               var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n"""
+               var html = """<h1>foo</h1>\n<h2>foo</h2>\n<h3>foo</h3>\n<h4>foo</h4>\n<h5>foo</h5>\n<h6>foo</h6>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test33 is test do
+               var md = """####### foo\n"""
+               var html = """<p>####### foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test34 is test do
+               var md = """#5 bolt\n\n#hashtag\n"""
+               var html = """<p>#5 bolt</p>\n<p>#hashtag</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test35 is test do
+               var md = """\\## foo\n"""
+               var html = """<p>## foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test36 is test do
+               var md = """# foo *bar* \\*baz\\*\n"""
+               var html = """<h1>foo <em>bar</em> *baz*</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test37 is test do
+               var md = """#                  foo                     \n"""
+               var html = """<h1>foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test38 is test do
+               var md = """ ### foo\n  ## foo\n   # foo\n"""
+               var html = """<h3>foo</h3>\n<h2>foo</h2>\n<h1>foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test39 is test do
+               var md = """    # foo\n"""
+               var html = """<pre><code># foo\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test40 is test do
+               var md = """foo\n    # bar\n"""
+               var html = """<p>foo\n# bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test41 is test do
+               var md = """## foo ##\n  ###   bar    ###\n"""
+               var html = """<h2>foo</h2>\n<h3>bar</h3>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test42 is test do
+               var md = """# foo ##################################\n##### foo ##\n"""
+               var html = """<h1>foo</h1>\n<h5>foo</h5>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test43 is test do
+               var md = """### foo ###     \n"""
+               var html = """<h3>foo</h3>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test44 is test do
+               var md = """### foo ### b\n"""
+               var html = """<h3>foo ### b</h3>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test45 is test do
+               var md = """# foo#\n"""
+               var html = """<h1>foo#</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test46 is test do
+               var md = """### foo \\###\n## foo #\\##\n# foo \\#\n"""
+               var html = """<h3>foo ###</h3>\n<h2>foo ###</h2>\n<h1>foo #</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test47 is test do
+               var md = """****\n## foo\n****\n"""
+               var html = """<hr />\n<h2>foo</h2>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test48 is test do
+               var md = """Foo bar\n# baz\nBar foo\n"""
+               var html = """<p>Foo bar</p>\n<h1>baz</h1>\n<p>Bar foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test49 is test do
+               var md = """## \n#\n### ###\n"""
+               var html = """<h2></h2>\n<h1></h1>\n<h3></h3>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_autolinks.nit b/lib/markdown2/tests/test_commonmark_autolinks.nit
new file mode 100644 (file)
index 0000000..04c718c
--- /dev/null
@@ -0,0 +1,136 @@
+# 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.
+
+module test_commonmark_autolinks is test
+
+import test_markdown
+
+class TestCommonmarkAutolinks
+       super TestMarkdownHtml
+       test
+
+       fun test568 is test do
+               var md = """<http://foo.bar.baz>\n"""
+               var html = """<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test569 is test do
+               var md = """<http://foo.bar.baz/test?q=hello&id=22&boolean>\n"""
+               var html = """<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test570 is test do
+               var md = """<irc://foo.bar:2233/baz>\n"""
+               var html = """<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test571 is test do
+               var md = """<MAILTO:FOO@BAR.BAZ>\n"""
+               var html = """<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test572 is test do
+               var md = """<a+b+c:d>\n"""
+               var html = """<p><a href="a+b+c:d">a+b+c:d</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test573 is test do
+               var md = """<made-up-scheme://foo,bar>\n"""
+               var html = """<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test574 is test do
+               var md = """<http://../>\n"""
+               var html = """<p><a href="http://../">http://../</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test575 is test do
+               var md = """<localhost:5001/foo>\n"""
+               var html = """<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test576 is test do
+               var md = """<http://foo.bar/baz bim>\n"""
+               var html = """<p>&lt;http://foo.bar/baz bim&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test577 is test do
+               var md = """<http://example.com/\\[\\>\n"""
+               var html = """<p><a href="http://example.com/%5C%5B%5C">http://example.com/\\[\\</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test578 is test do
+               var md = """<foo@bar.example.com>\n"""
+               var html = """<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test579 is test do
+               var md = """<foo+special@Bar.baz-bar0.com>\n"""
+               var html = """<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test580 is test do
+               var md = """<foo\\+@bar.example.com>\n"""
+               var html = """<p>&lt;foo+@bar.example.com&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test581 is test do
+               var md = """<>\n"""
+               var html = """<p>&lt;&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test582 is test do
+               var md = """< http://foo.bar >\n"""
+               var html = """<p>&lt; http://foo.bar &gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test583 is test do
+               var md = """<m:abc>\n"""
+               var html = """<p>&lt;m:abc&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test584 is test do
+               var md = """<foo.bar.baz>\n"""
+               var html = """<p>&lt;foo.bar.baz&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test585 is test do
+               var md = """http://example.com\n"""
+               var html = """<p>http://example.com</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test586 is test do
+               var md = """foo@bar.example.com\n"""
+               var html = """<p>foo@bar.example.com</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_backslash_escapes.nit b/lib/markdown2/tests/test_commonmark_backslash_escapes.nit
new file mode 100644 (file)
index 0000000..819158e
--- /dev/null
@@ -0,0 +1,100 @@
+# 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.
+
+module test_commonmark_backslash_escapes is test
+
+import test_markdown
+
+class TestCommonmarkBackslashEscapes
+       super TestMarkdownHtml
+       test
+
+       fun test291 is test do
+               var md = """\\!\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~\n"""
+               var html = """<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\\]^_`{|}~</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test292 is test do
+               var md = """\\\t\\A\\a\\ \\3\\φ\\«\n"""
+               var html = """<p>\\\t\\A\\a\\ \\3\\φ\\«</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test293 is test do
+               var md = """\\*not emphasized*\n\\<br/> not a tag\n\\[not a link](/foo)\n\\`not code`\n1\\. not a list\n\\* not a list\n\\# not a heading\n\\[foo]: /url "not a reference"\n"""
+               var html = """<p>*not emphasized*\n&lt;br/&gt; not a tag\n[not a link](/foo)\n`not code`\n1. not a list\n* not a list\n# not a heading\n[foo]: /url &quot;not a reference&quot;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test294 is test do
+               var md = """\\\\*emphasis*\n"""
+               var html = """<p>\\<em>emphasis</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test295 is test do
+               var md = """foo\\\nbar\n"""
+               var html = """<p>foo<br />\nbar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test296 is test do
+               var md = """`` \\[\\` ``\n"""
+               var html = """<p><code>\\[\\`</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test297 is test do
+               var md = """    \\[\\]\n"""
+               var html = """<pre><code>\\[\\]\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test298 is test do
+               var md = """~~~\n\\[\\]\n~~~\n"""
+               var html = """<pre><code>\\[\\]\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test299 is test do
+               var md = """<http://example.com?find=\\*>\n"""
+               var html = """<p><a href="http://example.com?find=%5C*">http://example.com?find=\\*</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test300 is test do
+               var md = """<a href="/bar\\/)">\n"""
+               var html = """<a href="/bar\\/)">\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test301 is test do
+               var md = """[foo](/bar\\* "ti\\*tle")\n"""
+               var html = """<p><a href="/bar*" title="ti*tle">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test302 is test do
+               var md = """[foo]\n\n[foo]: /bar\\* "ti\\*tle"\n"""
+               var html = """<p><a href="/bar*" title="ti*tle">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test303 is test do
+               var md = """``` foo\\+bar\nfoo\n```\n"""
+               var html = """<pre><code class="language-foo+bar">foo\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_blank_lines.nit b/lib/markdown2/tests/test_commonmark_blank_lines.nit
new file mode 100644 (file)
index 0000000..f294684
--- /dev/null
@@ -0,0 +1,28 @@
+# 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.
+
+module test_commonmark_blank_lines is test
+
+import test_markdown
+
+class TestCommonmarkBlankLines
+       super TestMarkdownHtml
+       test
+
+       fun test190 is test do
+               var md = """  \n\naaa\n  \n\n# aaa\n\n  \n"""
+               var html = """<p>aaa</p>\n<h1>aaa</h1>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_block_quotes.nit b/lib/markdown2/tests/test_commonmark_block_quotes.nit
new file mode 100644 (file)
index 0000000..db9542f
--- /dev/null
@@ -0,0 +1,172 @@
+# 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.
+
+module test_commonmark_block_quotes is test
+
+import test_markdown
+
+class TestCommonmarkBlockQuotes
+       super TestMarkdownHtml
+       test
+
+       fun test191 is test do
+               var md = """> # Foo\n> bar\n> baz\n"""
+               var html = """<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test192 is test do
+               var md = """># Foo\n>bar\n> baz\n"""
+               var html = """<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test193 is test do
+               var md = """   > # Foo\n   > bar\n > baz\n"""
+               var html = """<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test194 is test do
+               var md = """    > # Foo\n    > bar\n    > baz\n"""
+               var html = """<pre><code>&gt; # Foo\n&gt; bar\n&gt; baz\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test195 is test do
+               var md = """> # Foo\n> bar\nbaz\n"""
+               var html = """<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test196 is test do
+               var md = """> bar\nbaz\n> foo\n"""
+               var html = """<blockquote>\n<p>bar\nbaz\nfoo</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test197 is test do
+               var md = """> foo\n---\n"""
+               var html = """<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test198 is test do
+               var md = """> - foo\n- bar\n"""
+               var html = """<blockquote>\n<ul>\n<li>foo</li>\n</ul>\n</blockquote>\n<ul>\n<li>bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test199 is test do
+               var md = """>     foo\n    bar\n"""
+               var html = """<blockquote>\n<pre><code>foo\n</code></pre>\n</blockquote>\n<pre><code>bar\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test200 is test do
+               var md = """> ```\nfoo\n```\n"""
+               var html = """<blockquote>\n<pre><code></code></pre>\n</blockquote>\n<p>foo</p>\n<pre><code></code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test201 is test do
+               var md = """> foo\n    - bar\n"""
+               var html = """<blockquote>\n<p>foo\n- bar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test202 is test do
+               var md = """>\n"""
+               var html = """<blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test203 is test do
+               var md = """>\n>  \n> \n"""
+               var html = """<blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test204 is test do
+               var md = """>\n> foo\n>  \n"""
+               var html = """<blockquote>\n<p>foo</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test205 is test do
+               var md = """> foo\n\n> bar\n"""
+               var html = """<blockquote>\n<p>foo</p>\n</blockquote>\n<blockquote>\n<p>bar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test206 is test do
+               var md = """> foo\n> bar\n"""
+               var html = """<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test207 is test do
+               var md = """> foo\n>\n> bar\n"""
+               var html = """<blockquote>\n<p>foo</p>\n<p>bar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test208 is test do
+               var md = """foo\n> bar\n"""
+               var html = """<p>foo</p>\n<blockquote>\n<p>bar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test209 is test do
+               var md = """> aaa\n***\n> bbb\n"""
+               var html = """<blockquote>\n<p>aaa</p>\n</blockquote>\n<hr />\n<blockquote>\n<p>bbb</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test210 is test do
+               var md = """> bar\nbaz\n"""
+               var html = """<blockquote>\n<p>bar\nbaz</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test211 is test do
+               var md = """> bar\n\nbaz\n"""
+               var html = """<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test212 is test do
+               var md = """> bar\n>\nbaz\n"""
+               var html = """<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test213 is test do
+               var md = """> > > foo\nbar\n"""
+               var html = """<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n</blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test214 is test do
+               var md = """>>> foo\n> bar\n>>baz\n"""
+               var html = """<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar\nbaz</p>\n</blockquote>\n</blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test215 is test do
+               var md = """>     code\n\n>    not code\n"""
+               var html = """<blockquote>\n<pre><code>code\n</code></pre>\n</blockquote>\n<blockquote>\n<p>not code</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_code_spans.nit b/lib/markdown2/tests/test_commonmark_code_spans.nit
new file mode 100644 (file)
index 0000000..edcf96b
--- /dev/null
@@ -0,0 +1,124 @@
+# 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.
+
+module test_commonmark_code_spans is test
+
+import test_markdown
+
+class TestCommonmarkCodeSpans
+       super TestMarkdownHtml
+       test
+
+       fun test316 is test do
+               var md = """`foo`\n"""
+               var html = """<p><code>foo</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test317 is test do
+               var md = """`` foo ` bar  ``\n"""
+               var html = """<p><code>foo ` bar</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test318 is test do
+               var md = """` `` `\n"""
+               var html = """<p><code>``</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test319 is test do
+               var md = """``\nfoo\n``\n"""
+               var html = """<p><code>foo</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test320 is test do
+               var md = """`foo   bar\n  baz`\n"""
+               var html = """<p><code>foo bar baz</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test321 is test do
+               var md = """`a  b`\n"""
+               var html = """<p><code>a  b</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test322 is test do
+               var md = """`foo `` bar`\n"""
+               var html = """<p><code>foo `` bar</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test323 is test do
+               var md = """`foo\\`bar`\n"""
+               var html = """<p><code>foo\\</code>bar`</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test324 is test do
+               var md = """*foo`*`\n"""
+               var html = """<p>*foo<code>*</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test325 is test do
+               var md = """[not a `link](/foo`)\n"""
+               var html = """<p>[not a <code>link](/foo</code>)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test326 is test do
+               var md = """`<a href="`">`\n"""
+               var html = """<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test327 is test do
+               var md = """<a href="`">`\n"""
+               var html = """<p><a href="`">`</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test328 is test do
+               var md = """`<http://foo.bar.`baz>`\n"""
+               var html = """<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test329 is test do
+               var md = """<http://foo.bar.`baz>`\n"""
+               var html = """<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test330 is test do
+               var md = """```foo``\n"""
+               var html = """<p>```foo``</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test331 is test do
+               var md = """`foo\n"""
+               var html = """<p>`foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test332 is test do
+               var md = """`foo``bar``\n"""
+               var html = """<p>`foo<code>bar</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit b/lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit
new file mode 100644 (file)
index 0000000..64e317b
--- /dev/null
@@ -0,0 +1,796 @@
+# 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.
+
+module test_commonmark_emphasis_and_strong_emphasis is test
+
+import test_markdown
+
+class TestCommonmarkEmphasisAndStrongEmphasis
+       super TestMarkdownHtml
+       test
+
+       fun test333 is test do
+               var md = """*foo bar*\n"""
+               var html = """<p><em>foo bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test334 is test do
+               var md = """a * foo bar*\n"""
+               var html = """<p>a * foo bar*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test335 is test do
+               var md = """a*"foo"*\n"""
+               var html = """<p>a*&quot;foo&quot;*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test336 is test do
+               var md = """* a *\n"""
+               var html = """<p>* a *</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test337 is test do
+               var md = """foo*bar*\n"""
+               var html = """<p>foo<em>bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test338 is test do
+               var md = """5*6*78\n"""
+               var html = """<p>5<em>6</em>78</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test339 is test do
+               var md = """_foo bar_\n"""
+               var html = """<p><em>foo bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test340 is test do
+               var md = """_ foo bar_\n"""
+               var html = """<p>_ foo bar_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test341 is test do
+               var md = """a_"foo"_\n"""
+               var html = """<p>a_&quot;foo&quot;_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test342 is test do
+               var md = """foo_bar_\n"""
+               var html = """<p>foo_bar_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test343 is test do
+               var md = """5_6_78\n"""
+               var html = """<p>5_6_78</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test344 is test do
+               var md = """пристаням_стремятся_\n"""
+               var html = """<p>пристаням_стремятся_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test345 is test do
+               var md = """aa_"bb"_cc\n"""
+               var html = """<p>aa_&quot;bb&quot;_cc</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test346 is test do
+               var md = """foo-_(bar)_\n"""
+               var html = """<p>foo-<em>(bar)</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test347 is test do
+               var md = """_foo*\n"""
+               var html = """<p>_foo*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test348 is test do
+               var md = """*foo bar *\n"""
+               var html = """<p>*foo bar *</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test349 is test do
+               var md = """*foo bar\n*\n"""
+               var html = """<p>*foo bar\n*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test350 is test do
+               var md = """*(*foo)\n"""
+               var html = """<p>*(*foo)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test351 is test do
+               var md = """*(*foo*)*\n"""
+               var html = """<p><em>(<em>foo</em>)</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test352 is test do
+               var md = """*foo*bar\n"""
+               var html = """<p><em>foo</em>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test353 is test do
+               var md = """_foo bar _\n"""
+               var html = """<p>_foo bar _</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test354 is test do
+               var md = """_(_foo)\n"""
+               var html = """<p>_(_foo)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test355 is test do
+               var md = """_(_foo_)_\n"""
+               var html = """<p><em>(<em>foo</em>)</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test356 is test do
+               var md = """_foo_bar\n"""
+               var html = """<p>_foo_bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test357 is test do
+               var md = """_пристаням_стремятся\n"""
+               var html = """<p>_пристаням_стремятся</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test358 is test do
+               var md = """_foo_bar_baz_\n"""
+               var html = """<p><em>foo_bar_baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test359 is test do
+               var md = """_(bar)_.\n"""
+               var html = """<p><em>(bar)</em>.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test360 is test do
+               var md = """**foo bar**\n"""
+               var html = """<p><strong>foo bar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test361 is test do
+               var md = """** foo bar**\n"""
+               var html = """<p>** foo bar**</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test362 is test do
+               var md = """a**"foo"**\n"""
+               var html = """<p>a**&quot;foo&quot;**</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test363 is test do
+               var md = """foo**bar**\n"""
+               var html = """<p>foo<strong>bar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test364 is test do
+               var md = """__foo bar__\n"""
+               var html = """<p><strong>foo bar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test365 is test do
+               var md = """__ foo bar__\n"""
+               var html = """<p>__ foo bar__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test366 is test do
+               var md = """__\nfoo bar__\n"""
+               var html = """<p>__\nfoo bar__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test367 is test do
+               var md = """a__"foo"__\n"""
+               var html = """<p>a__&quot;foo&quot;__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test368 is test do
+               var md = """foo__bar__\n"""
+               var html = """<p>foo__bar__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test369 is test do
+               var md = """5__6__78\n"""
+               var html = """<p>5__6__78</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test370 is test do
+               var md = """пристаням__стремятся__\n"""
+               var html = """<p>пристаням__стремятся__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test371 is test do
+               var md = """__foo, __bar__, baz__\n"""
+               var html = """<p><strong>foo, <strong>bar</strong>, baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test372 is test do
+               var md = """foo-__(bar)__\n"""
+               var html = """<p>foo-<strong>(bar)</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test373 is test do
+               var md = """**foo bar **\n"""
+               var html = """<p>**foo bar **</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test374 is test do
+               var md = """**(**foo)\n"""
+               var html = """<p>**(**foo)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test375 is test do
+               var md = """*(**foo**)*\n"""
+               var html = """<p><em>(<strong>foo</strong>)</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test376 is test do
+               var md = """**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**\n"""
+               var html = """<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.\n<em>Asclepias physocarpa</em>)</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test377 is test do
+               var md = """**foo "*bar*" foo**\n"""
+               var html = """<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test378 is test do
+               var md = """**foo**bar\n"""
+               var html = """<p><strong>foo</strong>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test379 is test do
+               var md = """__foo bar __\n"""
+               var html = """<p>__foo bar __</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test380 is test do
+               var md = """__(__foo)\n"""
+               var html = """<p>__(__foo)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test381 is test do
+               var md = """_(__foo__)_\n"""
+               var html = """<p><em>(<strong>foo</strong>)</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test382 is test do
+               var md = """__foo__bar\n"""
+               var html = """<p>__foo__bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test383 is test do
+               var md = """__пристаням__стремятся\n"""
+               var html = """<p>__пристаням__стремятся</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test384 is test do
+               var md = """__foo__bar__baz__\n"""
+               var html = """<p><strong>foo__bar__baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test385 is test do
+               var md = """__(bar)__.\n"""
+               var html = """<p><strong>(bar)</strong>.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test386 is test do
+               var md = """*foo [bar](/url)*\n"""
+               var html = """<p><em>foo <a href="/url">bar</a></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test387 is test do
+               var md = """*foo\nbar*\n"""
+               var html = """<p><em>foo\nbar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test388 is test do
+               var md = """_foo __bar__ baz_\n"""
+               var html = """<p><em>foo <strong>bar</strong> baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test389 is test do
+               var md = """_foo _bar_ baz_\n"""
+               var html = """<p><em>foo <em>bar</em> baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test390 is test do
+               var md = """__foo_ bar_\n"""
+               var html = """<p><em><em>foo</em> bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test391 is test do
+               var md = """*foo *bar**\n"""
+               var html = """<p><em>foo <em>bar</em></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test392 is test do
+               var md = """*foo **bar** baz*\n"""
+               var html = """<p><em>foo <strong>bar</strong> baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test393 is test do
+               var md = """*foo**bar**baz*\n"""
+               var html = """<p><em>foo<strong>bar</strong>baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test394 is test do
+               var md = """*foo**bar*\n"""
+               var html = """<p><em>foo**bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test395 is test do
+               var md = """***foo** bar*\n"""
+               var html = """<p><em><strong>foo</strong> bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test396 is test do
+               var md = """*foo **bar***\n"""
+               var html = """<p><em>foo <strong>bar</strong></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test397 is test do
+               var md = """*foo**bar***\n"""
+               var html = """<p><em>foo<strong>bar</strong></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test398 is test do
+               var md = """*foo **bar *baz* bim** bop*\n"""
+               var html = """<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test399 is test do
+               var md = """*foo [*bar*](/url)*\n"""
+               var html = """<p><em>foo <a href="/url"><em>bar</em></a></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test400 is test do
+               var md = """** is not an empty emphasis\n"""
+               var html = """<p>** is not an empty emphasis</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test401 is test do
+               var md = """**** is not an empty strong emphasis\n"""
+               var html = """<p>**** is not an empty strong emphasis</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test402 is test do
+               var md = """**foo [bar](/url)**\n"""
+               var html = """<p><strong>foo <a href="/url">bar</a></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test403 is test do
+               var md = """**foo\nbar**\n"""
+               var html = """<p><strong>foo\nbar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test404 is test do
+               var md = """__foo _bar_ baz__\n"""
+               var html = """<p><strong>foo <em>bar</em> baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test405 is test do
+               var md = """__foo __bar__ baz__\n"""
+               var html = """<p><strong>foo <strong>bar</strong> baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test406 is test do
+               var md = """____foo__ bar__\n"""
+               var html = """<p><strong><strong>foo</strong> bar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test407 is test do
+               var md = """**foo **bar****\n"""
+               var html = """<p><strong>foo <strong>bar</strong></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test408 is test do
+               var md = """**foo *bar* baz**\n"""
+               var html = """<p><strong>foo <em>bar</em> baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test409 is test do
+               var md = """**foo*bar*baz**\n"""
+               var html = """<p><strong>foo<em>bar</em>baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test410 is test do
+               var md = """***foo* bar**\n"""
+               var html = """<p><strong><em>foo</em> bar</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test411 is test do
+               var md = """**foo *bar***\n"""
+               var html = """<p><strong>foo <em>bar</em></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test412 is test do
+               var md = """**foo *bar **baz**\nbim* bop**\n"""
+               var html = """<p><strong>foo <em>bar <strong>baz</strong>\nbim</em> bop</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test413 is test do
+               var md = """**foo [*bar*](/url)**\n"""
+               var html = """<p><strong>foo <a href="/url"><em>bar</em></a></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test414 is test do
+               var md = """__ is not an empty emphasis\n"""
+               var html = """<p>__ is not an empty emphasis</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test415 is test do
+               var md = """____ is not an empty strong emphasis\n"""
+               var html = """<p>____ is not an empty strong emphasis</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test416 is test do
+               var md = """foo ***\n"""
+               var html = """<p>foo ***</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test417 is test do
+               var md = """foo *\\**\n"""
+               var html = """<p>foo <em>*</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test418 is test do
+               var md = """foo *_*\n"""
+               var html = """<p>foo <em>_</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test419 is test do
+               var md = """foo *****\n"""
+               var html = """<p>foo *****</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test420 is test do
+               var md = """foo **\\***\n"""
+               var html = """<p>foo <strong>*</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test421 is test do
+               var md = """foo **_**\n"""
+               var html = """<p>foo <strong>_</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test422 is test do
+               var md = """**foo*\n"""
+               var html = """<p>*<em>foo</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test423 is test do
+               var md = """*foo**\n"""
+               var html = """<p><em>foo</em>*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test424 is test do
+               var md = """***foo**\n"""
+               var html = """<p>*<strong>foo</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test425 is test do
+               var md = """****foo*\n"""
+               var html = """<p>***<em>foo</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test426 is test do
+               var md = """**foo***\n"""
+               var html = """<p><strong>foo</strong>*</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test427 is test do
+               var md = """*foo****\n"""
+               var html = """<p><em>foo</em>***</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test428 is test do
+               var md = """foo ___\n"""
+               var html = """<p>foo ___</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test429 is test do
+               var md = """foo _\\__\n"""
+               var html = """<p>foo <em>_</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test430 is test do
+               var md = """foo _*_\n"""
+               var html = """<p>foo <em>*</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test431 is test do
+               var md = """foo _____\n"""
+               var html = """<p>foo _____</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test432 is test do
+               var md = """foo __\\___\n"""
+               var html = """<p>foo <strong>_</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test433 is test do
+               var md = """foo __*__\n"""
+               var html = """<p>foo <strong>*</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test434 is test do
+               var md = """__foo_\n"""
+               var html = """<p>_<em>foo</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test435 is test do
+               var md = """_foo__\n"""
+               var html = """<p><em>foo</em>_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test436 is test do
+               var md = """___foo__\n"""
+               var html = """<p>_<strong>foo</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test437 is test do
+               var md = """____foo_\n"""
+               var html = """<p>___<em>foo</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test438 is test do
+               var md = """__foo___\n"""
+               var html = """<p><strong>foo</strong>_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test439 is test do
+               var md = """_foo____\n"""
+               var html = """<p><em>foo</em>___</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test440 is test do
+               var md = """**foo**\n"""
+               var html = """<p><strong>foo</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test441 is test do
+               var md = """*_foo_*\n"""
+               var html = """<p><em><em>foo</em></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test442 is test do
+               var md = """__foo__\n"""
+               var html = """<p><strong>foo</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test443 is test do
+               var md = """_*foo*_\n"""
+               var html = """<p><em><em>foo</em></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test444 is test do
+               var md = """****foo****\n"""
+               var html = """<p><strong><strong>foo</strong></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test445 is test do
+               var md = """____foo____\n"""
+               var html = """<p><strong><strong>foo</strong></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test446 is test do
+               var md = """******foo******\n"""
+               var html = """<p><strong><strong><strong>foo</strong></strong></strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test447 is test do
+               var md = """***foo***\n"""
+               var html = """<p><em><strong>foo</strong></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test448 is test do
+               var md = """_____foo_____\n"""
+               var html = """<p><em><strong><strong>foo</strong></strong></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test449 is test do
+               var md = """*foo _bar* baz_\n"""
+               var html = """<p><em>foo _bar</em> baz_</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test450 is test do
+               var md = """*foo __bar *baz bim__ bam*\n"""
+               var html = """<p><em>foo <strong>bar *baz bim</strong> bam</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test451 is test do
+               var md = """**foo **bar baz**\n"""
+               var html = """<p>**foo <strong>bar baz</strong></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test452 is test do
+               var md = """*foo *bar baz*\n"""
+               var html = """<p>*foo <em>bar baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test453 is test do
+               var md = """*[bar*](/url)\n"""
+               var html = """<p>*<a href="/url">bar*</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test454 is test do
+               var md = """_foo [bar_](/url)\n"""
+               var html = """<p>_foo <a href="/url">bar_</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test455 is test do
+               var md = """*<img src="foo" title="*"/>\n"""
+               var html = """<p>*<img src="foo" title="*"/></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test456 is test do
+               var md = """**<a href="**">\n"""
+               var html = """<p>**<a href="**"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test457 is test do
+               var md = """__<a href="__">\n"""
+               var html = """<p>__<a href="__"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test458 is test do
+               var md = """*a `*`*\n"""
+               var html = """<p><em>a <code>*</code></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test459 is test do
+               var md = """_a `_`_\n"""
+               var html = """<p><em>a <code>_</code></em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test460 is test do
+               var md = """**a<http://foo.bar/?q=**>\n"""
+               var html = """<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test461 is test do
+               var md = """__a<http://foo.bar/?q=__>\n"""
+               var html = """<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit b/lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit
new file mode 100644 (file)
index 0000000..f61edd4
--- /dev/null
@@ -0,0 +1,58 @@
+# 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.
+
+module test_commonmark_entity_and_numeric_character_references is test
+
+import test_markdown
+
+class TestCommonmarkEntityAndNumericCharacterReferences
+       super TestMarkdownHtml
+       test
+
+       fun test307 is test do
+               var md = """&nbsp &x; &#; &#x;\n&#987654321;\n&#abcdef0;\n&ThisIsNotDefined; &hi?;\n"""
+               var html = """<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;\n&amp;#987654321;\n&amp;#abcdef0;\n&amp;ThisIsNotDefined; &amp;hi?;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test308 is test do
+               var md = """&copy\n"""
+               var html = """<p>&amp;copy</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test309 is test do
+               var md = """&MadeUpEntity;\n"""
+               var html = """<p>&amp;MadeUpEntity;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test310 is test do
+               var md = """<a href="&ouml;&ouml;.html">\n"""
+               var html = """<a href="&ouml;&ouml;.html">\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test314 is test do
+               var md = """`f&ouml;&ouml;`\n"""
+               var html = """<p><code>f&amp;ouml;&amp;ouml;</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test315 is test do
+               var md = """    f&ouml;f&ouml;\n"""
+               var html = """<pre><code>f&amp;ouml;f&amp;ouml;\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit b/lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit
new file mode 100644 (file)
index 0000000..2d7072a
--- /dev/null
@@ -0,0 +1,190 @@
+# 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.
+
+module test_commonmark_fenced_code_blocks is test
+
+import test_markdown
+
+class TestCommonmarkFencedCodeBlocks
+       super TestMarkdownHtml
+       test
+
+       fun test88 is test do
+               var md = """```\n<\n >\n```\n"""
+               var html = """<pre><code>&lt;\n &gt;\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test89 is test do
+               var md = """~~~\n<\n >\n~~~\n"""
+               var html = """<pre><code>&lt;\n &gt;\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test90 is test do
+               var md = """``\nfoo\n``\n"""
+               var html = """<p><code>foo</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test91 is test do
+               var md = """```\naaa\n~~~\n```\n"""
+               var html = """<pre><code>aaa\n~~~\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test92 is test do
+               var md = """~~~\naaa\n```\n~~~\n"""
+               var html = """<pre><code>aaa\n```\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test93 is test do
+               var md = """````\naaa\n```\n``````\n"""
+               var html = """<pre><code>aaa\n```\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test94 is test do
+               var md = """~~~~\naaa\n~~~\n~~~~\n"""
+               var html = """<pre><code>aaa\n~~~\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test95 is test do
+               var md = """```\n"""
+               var html = """<pre><code></code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test96 is test do
+               var md = """`````\n\n```\naaa\n"""
+               var html = """<pre><code>\n```\naaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test97 is test do
+               var md = """> ```\n> aaa\n\nbbb\n"""
+               var html = """<blockquote>\n<pre><code>aaa\n</code></pre>\n</blockquote>\n<p>bbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test98 is test do
+               var md = """```\n\n  \n```\n"""
+               var html = """<pre><code>\n  \n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test99 is test do
+               var md = """```\n```\n"""
+               var html = """<pre><code></code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test100 is test do
+               var md = """ ```\n aaa\naaa\n```\n"""
+               var html = """<pre><code>aaa\naaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test101 is test do
+               var md = """  ```\naaa\n  aaa\naaa\n  ```\n"""
+               var html = """<pre><code>aaa\naaa\naaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test102 is test do
+               var md = """   ```\n   aaa\n    aaa\n  aaa\n   ```\n"""
+               var html = """<pre><code>aaa\n aaa\naaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test103 is test do
+               var md = """    ```\n    aaa\n    ```\n"""
+               var html = """<pre><code>```\naaa\n```\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test104 is test do
+               var md = """```\naaa\n  ```\n"""
+               var html = """<pre><code>aaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test105 is test do
+               var md = """   ```\naaa\n  ```\n"""
+               var html = """<pre><code>aaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test106 is test do
+               var md = """```\naaa\n    ```\n"""
+               var html = """<pre><code>aaa\n    ```\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test107 is test do
+               var md = """``` ```\naaa\n"""
+               var html = """<p><code></code>\naaa</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test108 is test do
+               var md = """~~~~~~\naaa\n~~~ ~~\n"""
+               var html = """<pre><code>aaa\n~~~ ~~\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test109 is test do
+               var md = """foo\n```\nbar\n```\nbaz\n"""
+               var html = """<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test110 is test do
+               var md = """foo\n---\n~~~\nbar\n~~~\n# baz\n"""
+               var html = """<h2>foo</h2>\n<pre><code>bar\n</code></pre>\n<h1>baz</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test111 is test do
+               var md = """```ruby\ndef foo(x)\n  return 3\nend\n```\n"""
+               var html = """<pre><code class="language-ruby">def foo(x)\n  return 3\nend\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test112 is test do
+               var md = """~~~~    ruby startline=3 $%@#$\ndef foo(x)\n  return 3\nend\n~~~~~~~\n"""
+               var html = """<pre><code class="language-ruby">def foo(x)\n  return 3\nend\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test113 is test do
+               var md = """````;\n````\n"""
+               var html = """<pre><code class="language-;"></code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test114 is test do
+               var md = """``` aa ```\nfoo\n"""
+               var html = """<p><code>aa</code>\nfoo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test115 is test do
+               var md = """```\n``` aaa\n```\n"""
+               var html = """<pre><code>``` aaa\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_hard_line_breaks.nit b/lib/markdown2/tests/test_commonmark_hard_line_breaks.nit
new file mode 100644 (file)
index 0000000..7e2336f
--- /dev/null
@@ -0,0 +1,112 @@
+# 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.
+
+module test_commonmark_hard_line_breaks is test
+
+import test_markdown
+
+class TestCommonmarkHardLineBreaks
+       super TestMarkdownHtml
+       test
+
+       fun test608 is test do
+               var md = """foo  \nbaz\n"""
+               var html = """<p>foo<br />\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test609 is test do
+               var md = """foo\\\nbaz\n"""
+               var html = """<p>foo<br />\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test610 is test do
+               var md = """foo       \nbaz\n"""
+               var html = """<p>foo<br />\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test611 is test do
+               var md = """foo  \n     bar\n"""
+               var html = """<p>foo<br />\nbar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test612 is test do
+               var md = """foo\\\n     bar\n"""
+               var html = """<p>foo<br />\nbar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test613 is test do
+               var md = """*foo  \nbar*\n"""
+               var html = """<p><em>foo<br />\nbar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test614 is test do
+               var md = """*foo\\\nbar*\n"""
+               var html = """<p><em>foo<br />\nbar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test615 is test do
+               var md = """`code  \nspan`\n"""
+               var html = """<p><code>code span</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test616 is test do
+               var md = """`code\\\nspan`\n"""
+               var html = """<p><code>code\\ span</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test617 is test do
+               var md = """<a href="foo  \nbar">\n"""
+               var html = """<p><a href="foo  \nbar"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test618 is test do
+               var md = """<a href="foo\\\nbar">\n"""
+               var html = """<p><a href="foo\\\nbar"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test619 is test do
+               var md = """foo\\\n"""
+               var html = """<p>foo\\</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test620 is test do
+               var md = """foo  \n"""
+               var html = """<p>foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test621 is test do
+               var md = """### foo\\\n"""
+               var html = """<h3>foo\\</h3>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test622 is test do
+               var md = """### foo  \n"""
+               var html = """<h3>foo</h3>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_html_blocks.nit b/lib/markdown2/tests/test_commonmark_html_blocks.nit
new file mode 100644 (file)
index 0000000..5b92555
--- /dev/null
@@ -0,0 +1,280 @@
+# 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.
+
+module test_commonmark_html_blocks is test
+
+import test_markdown
+
+class TestCommonmarkHTMLBlocks
+       super TestMarkdownHtml
+       test
+
+       fun test116 is test do
+               var md = """<table><tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr></table>\n"""
+               var html = """<table><tr><td>\n<pre>\n**Hello**,\n<p><em>world</em>.\n</pre></p>\n</td></tr></table>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test117 is test do
+               var md = """<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n\nokay.\n"""
+               var html = """<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n<p>okay.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test118 is test do
+               var md = """ <div>\n  *hello*\n         <foo><a>\n"""
+               var html = """ <div>\n  *hello*\n         <foo><a>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test119 is test do
+               var md = """</div>\n*foo*\n"""
+               var html = """</div>\n*foo*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test120 is test do
+               var md = """<DIV CLASS="foo">\n\n*Markdown*\n\n</DIV>\n"""
+               var html = """<DIV CLASS="foo">\n<p><em>Markdown</em></p>\n</DIV>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test121 is test do
+               var md = """<div id="foo"\n  class="bar">\n</div>\n"""
+               var html = """<div id="foo"\n  class="bar">\n</div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test122 is test do
+               var md = """<div id="foo" class="bar\n  baz">\n</div>\n"""
+               var html = """<div id="foo" class="bar\n  baz">\n</div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test123 is test do
+               var md = """<div>\n*foo*\n\n*bar*\n"""
+               var html = """<div>\n*foo*\n<p><em>bar</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test124 is test do
+               var md = """<div id="foo"\n*hi*\n"""
+               var html = """<div id="foo"\n*hi*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test125 is test do
+               var md = """<div class\nfoo\n"""
+               var html = """<div class\nfoo\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test126 is test do
+               var md = """<div *???-&&&-<---\n*foo*\n"""
+               var html = """<div *???-&&&-<---\n*foo*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test127 is test do
+               var md = """<div><a href="bar">*foo*</a></div>\n"""
+               var html = """<div><a href="bar">*foo*</a></div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test128 is test do
+               var md = """<table><tr><td>\nfoo\n</td></tr></table>\n"""
+               var html = """<table><tr><td>\nfoo\n</td></tr></table>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test129 is test do
+               var md = """<div></div>\n``` c\nint x = 33;\n```\n"""
+               var html = """<div></div>\n``` c\nint x = 33;\n```\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test130 is test do
+               var md = """<a href="foo">\n*bar*\n</a>\n"""
+               var html = """<a href="foo">\n*bar*\n</a>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test131 is test do
+               var md = """<Warning>\n*bar*\n</Warning>\n"""
+               var html = """<Warning>\n*bar*\n</Warning>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test132 is test do
+               var md = """<i class="foo">\n*bar*\n</i>\n"""
+               var html = """<i class="foo">\n*bar*\n</i>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test133 is test do
+               var md = """</ins>\n*bar*\n"""
+               var html = """</ins>\n*bar*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test134 is test do
+               var md = """<del>\n*foo*\n</del>\n"""
+               var html = """<del>\n*foo*\n</del>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test135 is test do
+               var md = """<del>\n\n*foo*\n\n</del>\n"""
+               var html = """<del>\n<p><em>foo</em></p>\n</del>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test136 is test do
+               var md = """<del>*foo*</del>\n"""
+               var html = """<p><del><em>foo</em></del></p>\n"""
+               assert md_to_html(md) == html
+       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>\nokay\n"""
+               var html = """<pre language="haskell"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\n<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test138 is test do
+               var md = """<script type="text/javascript">\n// JavaScript example\n\ndocument.getElementById("demo").innerHTML = "Hello JavaScript!";\n</script>\nokay\n"""
+               var html = """<script type="text/javascript">\n// JavaScript example\n\ndocument.getElementById("demo").innerHTML = "Hello JavaScript!";\n</script>\n<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test139 is test do
+               var md = """<style\n  type="text/css">\nh1 {color:red;}\n\np {color:blue;}\n</style>\nokay\n"""
+               var html = """<style\n  type="text/css">\nh1 {color:red;}\n\np {color:blue;}\n</style>\n<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test140 is test do
+               var md = """<style\n  type="text/css">\n\nfoo\n"""
+               var html = """<style\n  type="text/css">\n\nfoo\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test141 is test do
+               var md = """> <div>\n> foo\n\nbar\n"""
+               var html = """<blockquote>\n<div>\nfoo\n</blockquote>\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test142 is test do
+               var md = """- <div>\n- foo\n"""
+               var html = """<ul>\n<li>\n<div>\n</li>\n<li>foo</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test143 is test do
+               var md = """<style>p{color:red;}</style>\n*foo*\n"""
+               var html = """<style>p{color:red;}</style>\n<p><em>foo</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test144 is test do
+               var md = """<!-- foo -->*bar*\n*baz*\n"""
+               var html = """<!-- foo -->*bar*\n<p><em>baz</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test145 is test do
+               var md = """<script>\nfoo\n</script>1. *bar*\n"""
+               var html = """<script>\nfoo\n</script>1. *bar*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test146 is test do
+               var md = """<!-- Foo\n\nbar\n   baz -->\nokay\n"""
+               var html = """<!-- Foo\n\nbar\n   baz -->\n<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test147 is test do
+               var md = """<?php\n\n  echo '>';\n\n?>\nokay\n"""
+               var html = """<?php\n\n  echo '>';\n\n?>\n<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test148 is test do
+               var md = """<!DOCTYPE html>\n"""
+               var html = """<!DOCTYPE html>\n"""
+               assert md_to_html(md) == html
+       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]]>\nokay\n"""
+               var html = """<![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<p>okay</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test150 is test do
+               var md = """  <!-- foo -->\n\n    <!-- foo -->\n"""
+               var html = """  <!-- foo -->\n<pre><code>&lt;!-- foo --&gt;\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test151 is test do
+               var md = """  <div>\n\n    <div>\n"""
+               var html = """  <div>\n<pre><code>&lt;div&gt;\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test152 is test do
+               var md = """Foo\n<div>\nbar\n</div>\n"""
+               var html = """<p>Foo</p>\n<div>\nbar\n</div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test153 is test do
+               var md = """<div>\nbar\n</div>\n*foo*\n"""
+               var html = """<div>\nbar\n</div>\n*foo*\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test154 is test do
+               var md = """Foo\n<a href="bar">\nbaz\n"""
+               var html = """<p>Foo\n<a href="bar">\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test155 is test do
+               var md = """<div>\n\n*Emphasized* text.\n\n</div>\n"""
+               var html = """<div>\n<p><em>Emphasized</em> text.</p>\n</div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test156 is test do
+               var md = """<div>\n*Emphasized* text.\n</div>\n"""
+               var html = """<div>\n*Emphasized* text.\n</div>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test157 is test do
+               var md = """<table>\n\n<tr>\n\n<td>\nHi\n</td>\n\n</tr>\n\n</table>\n"""
+               var html = """<table>\n<tr>\n<td>\nHi\n</td>\n</tr>\n</table>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test158 is test do
+               var md = """<table>\n\n  <tr>\n\n    <td>\n      Hi\n    </td>\n\n  </tr>\n\n</table>\n"""
+               var html = """<table>\n  <tr>\n<pre><code>&lt;td&gt;\n  Hi\n&lt;/td&gt;\n</code></pre>\n  </tr>\n</table>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_images.nit b/lib/markdown2/tests/test_commonmark_images.nit
new file mode 100644 (file)
index 0000000..e75f4c1
--- /dev/null
@@ -0,0 +1,154 @@
+# 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.
+
+module test_commonmark_images is test
+
+import test_markdown
+
+class TestCommonmarkImages
+       super TestMarkdownHtml
+       test
+
+       fun test546 is test do
+               var md = """![foo](/url "title")\n"""
+               var html = """<p><img src="/url" alt="foo" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test547 is test do
+               var md = """![foo *bar*]\n\n[foo *bar*]: train.jpg "train & tracks"\n"""
+               var html = """<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test548 is test do
+               var md = """![foo ![bar](/url)](/url2)\n"""
+               var html = """<p><img src="/url2" alt="foo bar" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test549 is test do
+               var md = """![foo [bar](/url)](/url2)\n"""
+               var html = """<p><img src="/url2" alt="foo bar" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test550 is test do
+               var md = """![foo *bar*][]\n\n[foo *bar*]: train.jpg "train & tracks"\n"""
+               var html = """<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test551 is test do
+               var md = """![foo *bar*][foobar]\n\n[FOOBAR]: train.jpg "train & tracks"\n"""
+               var html = """<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test552 is test do
+               var md = """![foo](train.jpg)\n"""
+               var html = """<p><img src="train.jpg" alt="foo" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test553 is test do
+               var md = """My ![foo bar](/path/to/train.jpg  "title"   )\n"""
+               var html = """<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test554 is test do
+               var md = """![foo](<url>)\n"""
+               var html = """<p><img src="url" alt="foo" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test555 is test do
+               var md = """![](/url)\n"""
+               var html = """<p><img src="/url" alt="" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test556 is test do
+               var md = """![foo][bar]\n\n[bar]: /url\n"""
+               var html = """<p><img src="/url" alt="foo" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test557 is test do
+               var md = """![foo][bar]\n\n[BAR]: /url\n"""
+               var html = """<p><img src="/url" alt="foo" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test558 is test do
+               var md = """![foo][]\n\n[foo]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="foo" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test559 is test do
+               var md = """![*foo* bar][]\n\n[*foo* bar]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="foo bar" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test560 is test do
+               var md = """![Foo][]\n\n[foo]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="Foo" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test561 is test do
+               var md = """![foo] \n[]\n\n[foo]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="foo" title="title" />\n[]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test562 is test do
+               var md = """![foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="foo" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test563 is test do
+               var md = """![*foo* bar]\n\n[*foo* bar]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="foo bar" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test564 is test do
+               var md = """![[foo]]\n\n[[foo]]: /url "title"\n"""
+               var html = """<p>![[foo]]</p>\n<p>[[foo]]: /url &quot;title&quot;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test565 is test do
+               var md = """![Foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p><img src="/url" alt="Foo" title="title" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test566 is test do
+               var md = """!\\[foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p>![foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test567 is test do
+               var md = """\\![foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p>!<a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_indented_code_blocks.nit b/lib/markdown2/tests/test_commonmark_indented_code_blocks.nit
new file mode 100644 (file)
index 0000000..0c8bf2f
--- /dev/null
@@ -0,0 +1,94 @@
+# 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.
+
+module test_commonmark_indented_code_blocks is test
+
+import test_markdown
+
+class TestCommonmarkIndentedCodeBlocks
+       super TestMarkdownHtml
+       test
+
+       fun test76 is test do
+               var md = """    a simple\n      indented code block\n"""
+               var html = """<pre><code>a simple\n  indented code block\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test77 is test do
+               var md = """  - foo\n\n    bar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test78 is test do
+               var md = """1.  foo\n\n    - bar\n"""
+               var html = """<ol>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test79 is test do
+               var md = """    <a/>\n    *hi*\n\n    - one\n"""
+               var html = """<pre><code>&lt;a/&gt;\n*hi*\n\n- one\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test80 is test do
+               var md = """    chunk1\n\n    chunk2\n  \n \n \n    chunk3\n"""
+               var html = """<pre><code>chunk1\n\nchunk2\n\n\n\nchunk3\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test81 is test do
+               var md = """    chunk1\n      \n      chunk2\n"""
+               var html = """<pre><code>chunk1\n  \n  chunk2\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test82 is test do
+               var md = """Foo\n    bar\n\n"""
+               var html = """<p>Foo\nbar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test83 is test do
+               var md = """    foo\nbar\n"""
+               var html = """<pre><code>foo\n</code></pre>\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test84 is test do
+               var md = """# Heading\n    foo\nHeading\n------\n    foo\n----\n"""
+               var html = """<h1>Heading</h1>\n<pre><code>foo\n</code></pre>\n<h2>Heading</h2>\n<pre><code>foo\n</code></pre>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test85 is test do
+               var md = """        foo\n    bar\n"""
+               var html = """<pre><code>    foo\nbar\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test86 is test do
+               var md = """\n    \n    foo\n    \n\n"""
+               var html = """<pre><code>foo\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test87 is test do
+               var md = """    foo  \n"""
+               var html = """<pre><code>foo  \n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_inlines.nit b/lib/markdown2/tests/test_commonmark_inlines.nit
new file mode 100644 (file)
index 0000000..ac13156
--- /dev/null
@@ -0,0 +1,28 @@
+# 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.
+
+module test_commonmark_inlines is test
+
+import test_markdown
+
+class TestCommonmarkInlines
+       super TestMarkdownHtml
+       test
+
+       fun test290 is test do
+               var md = """`hi`lo`\n"""
+               var html = """<p><code>hi</code>lo`</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_link_reference_definitions.nit b/lib/markdown2/tests/test_commonmark_link_reference_definitions.nit
new file mode 100644 (file)
index 0000000..ebd19a4
--- /dev/null
@@ -0,0 +1,154 @@
+# 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.
+
+module test_commonmark_link_reference_definitions is test
+
+import test_markdown
+
+class TestCommonmarkLinkReferenceDefinitions
+       super TestMarkdownHtml
+       test
+
+       fun test159 is test do
+               var md = """[foo]: /url "title"\n\n[foo]\n"""
+               var html = """<p><a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test160 is test do
+               var md = """   [foo]: \n      /url  \n           'the title'  \n\n[foo]\n"""
+               var html = """<p><a href="/url" title="the title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test161 is test do
+               var md = """[Foo*bar\\]]:my_(url) 'title (with parens)'\n\n[Foo*bar\\]]\n"""
+               var html = """<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test162 is test do
+               var md = """[Foo bar]:\n<my%20url>\n'title'\n\n[Foo bar]\n"""
+               var html = """<p><a href="my%20url" title="title">Foo bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test163 is test do
+               var md = """[foo]: /url '\ntitle\nline1\nline2\n'\n\n[foo]\n"""
+               var html = """<p><a href="/url" title="\ntitle\nline1\nline2\n">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test164 is test do
+               var md = """[foo]: /url 'title\n\nwith blank line'\n\n[foo]\n"""
+               var html = """<p>[foo]: /url 'title</p>\n<p>with blank line'</p>\n<p>[foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test165 is test do
+               var md = """[foo]:\n/url\n\n[foo]\n"""
+               var html = """<p><a href="/url">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test166 is test do
+               var md = """[foo]:\n\n[foo]\n"""
+               var html = """<p>[foo]:</p>\n<p>[foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test167 is test do
+               var md = """[foo]: /url\\bar\\*baz "foo\\"bar\\baz"\n\n[foo]\n"""
+               var html = """<p><a href="/url%5Cbar*baz" title="foo&quot;bar\\baz">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test168 is test do
+               var md = """[foo]\n\n[foo]: url\n"""
+               var html = """<p><a href="url">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test169 is test do
+               var md = """[foo]\n\n[foo]: first\n[foo]: second\n"""
+               var html = """<p><a href="first">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test170 is test do
+               var md = """[FOO]: /url\n\n[Foo]\n"""
+               var html = """<p><a href="/url">Foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test172 is test do
+               var md = """[foo]: /url\n"""
+               var html = """"""
+               assert md_to_html(md) == html
+       end
+
+       fun test173 is test do
+               var md = """[\nfoo\n]: /url\nbar\n"""
+               var html = """<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test174 is test do
+               var md = """[foo]: /url "title" ok\n"""
+               var html = """<p>[foo]: /url &quot;title&quot; ok</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test175 is test do
+               var md = """[foo]: /url\n"title" ok\n"""
+               var html = """<p>&quot;title&quot; ok</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test176 is test do
+               var md = """    [foo]: /url "title"\n\n[foo]\n"""
+               var html = """<pre><code>[foo]: /url &quot;title&quot;\n</code></pre>\n<p>[foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test177 is test do
+               var md = """```\n[foo]: /url\n```\n\n[foo]\n"""
+               var html = """<pre><code>[foo]: /url\n</code></pre>\n<p>[foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test178 is test do
+               var md = """Foo\n[bar]: /baz\n\n[bar]\n"""
+               var html = """<p>Foo\n[bar]: /baz</p>\n<p>[bar]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test179 is test do
+               var md = """# [Foo]\n[foo]: /url\n> bar\n"""
+               var html = """<h1><a href="/url">Foo</a></h1>\n<blockquote>\n<p>bar</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test180 is test do
+               var md = """[foo]: /foo-url "foo"\n[bar]: /bar-url\n  "bar"\n[baz]: /baz-url\n\n[foo],\n[bar],\n[baz]\n"""
+               var html = """<p><a href="/foo-url" title="foo">foo</a>,\n<a href="/bar-url" title="bar">bar</a>,\n<a href="/baz-url">baz</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test181 is test do
+               var md = """[foo]\n\n> [foo]: /url\n"""
+               var html = """<p><a href="/url">foo</a></p>\n<blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_links.nit b/lib/markdown2/tests/test_commonmark_links.nit
new file mode 100644 (file)
index 0000000..bedbbbd
--- /dev/null
@@ -0,0 +1,514 @@
+# 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.
+
+module test_commonmark_links is test
+
+import test_markdown
+
+class TestCommonmarkLinks
+       super TestMarkdownHtml
+       test
+
+       fun test462 is test do
+               var md = """[link](/uri "title")\n"""
+               var html = """<p><a href="/uri" title="title">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test463 is test do
+               var md = """[link](/uri)\n"""
+               var html = """<p><a href="/uri">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test464 is test do
+               var md = """[link]()\n"""
+               var html = """<p><a href="">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test465 is test do
+               var md = """[link](<>)\n"""
+               var html = """<p><a href="">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test466 is test do
+               var md = """[link](/my uri)\n"""
+               var html = """<p>[link](/my uri)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test467 is test do
+               var md = """[link](</my%20uri>)\n"""
+               var html = """<p><a href="/my%20uri">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test468 is test do
+               var md = """[link](foo\nbar)\n"""
+               var html = """<p>[link](foo\nbar)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test469 is test do
+               var md = """[link](<foo\nbar>)\n"""
+               var html = """<p>[link](<foo\nbar>)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test470 is test do
+               var md = """[link](\\(foo\\))\n"""
+               var html = """<p><a href="(foo)">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test471 is test do
+               var md = """[link](foo(and(bar)))\n"""
+               var html = """<p><a href="foo(and(bar))">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test472 is test do
+               var md = """[link](foo\\(and\\(bar\\))\n"""
+               var html = """<p><a href="foo(and(bar)">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test473 is test do
+               var md = """[link](<foo(and(bar)>)\n"""
+               var html = """<p><a href="foo(and(bar)">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test474 is test do
+               var md = """[link](foo\\)\\:)\n"""
+               var html = """<p><a href="foo):">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test475 is test do
+               var md = """[link](#fragment)\n\n[link](http://example.com#fragment)\n\n[link](http://example.com?foo=3#frag)\n"""
+               var html = """<p><a href="#fragment">link</a></p>\n<p><a href="http://example.com#fragment">link</a></p>\n<p><a href="http://example.com?foo=3#frag">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test476 is test do
+               var md = """[link](foo\\bar)\n"""
+               var html = """<p><a href="foo%5Cbar">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test478 is test do
+               var md = """[link]("title")\n"""
+               var html = """<p><a href="%22title%22">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test479 is test do
+               var md = """[link](/url "title")\n[link](/url 'title')\n[link](/url (title))\n"""
+               var html = """<p><a href="/url" title="title">link</a>\n<a href="/url" title="title">link</a>\n<a href="/url" title="title">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test480 is test do
+               var md = """[link](/url "title \\"&quot;")\n"""
+               var html = """<p><a href="/url" title="title &quot;&quot;">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test481 is test do
+               var md = """[link](/url "title")\n"""
+               var html = """<p><a href="/url%C2%A0%22title%22">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test482 is test do
+               var md = """[link](/url "title "and" title")\n"""
+               var html = """<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test483 is test do
+               var md = """[link](/url 'title "and" title')\n"""
+               var html = """<p><a href="/url" title="title &quot;and&quot; title">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test484 is test do
+               var md = """[link](   /uri\n  "title"  )\n"""
+               var html = """<p><a href="/uri" title="title">link</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test485 is test do
+               var md = """[link] (/uri)\n"""
+               var html = """<p>[link] (/uri)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test486 is test do
+               var md = """[link [foo [bar]]](/uri)\n"""
+               var html = """<p><a href="/uri">link [foo [bar]]</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test487 is test do
+               var md = """[link] bar](/uri)\n"""
+               var html = """<p>[link] bar](/uri)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test488 is test do
+               var md = """[link [bar](/uri)\n"""
+               var html = """<p>[link <a href="/uri">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test489 is test do
+               var md = """[link \\[bar](/uri)\n"""
+               var html = """<p><a href="/uri">link [bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test490 is test do
+               var md = """[link *foo **bar** `#`*](/uri)\n"""
+               var html = """<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test491 is test do
+               var md = """[![moon](moon.jpg)](/uri)\n"""
+               var html = """<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test492 is test do
+               var md = """[foo [bar](/uri)](/uri)\n"""
+               var html = """<p>[foo <a href="/uri">bar</a>](/uri)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test493 is test do
+               var md = """[foo *[bar [baz](/uri)](/uri)*](/uri)\n"""
+               var html = """<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test494 is test do
+               var md = """![[[foo](uri1)](uri2)](uri3)\n"""
+               var html = """<p><img src="uri3" alt="[foo](uri2)" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test495 is test do
+               var md = """*[foo*](/uri)\n"""
+               var html = """<p>*<a href="/uri">foo*</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test496 is test do
+               var md = """[foo *bar](baz*)\n"""
+               var html = """<p><a href="baz*">foo *bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test497 is test do
+               var md = """*foo [bar* baz]\n"""
+               var html = """<p><em>foo [bar</em> baz]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test498 is test do
+               var md = """[foo <bar attr="](baz)">\n"""
+               var html = """<p>[foo <bar attr="](baz)"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test499 is test do
+               var md = """[foo`](/uri)`\n"""
+               var html = """<p>[foo<code>](/uri)</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test500 is test do
+               var md = """[foo<http://example.com/?search=](uri)>\n"""
+               var html = """<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test501 is test do
+               var md = """[foo][bar]\n\n[bar]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test502 is test do
+               var md = """[link [foo [bar]]][ref]\n\n[ref]: /uri\n"""
+               var html = """<p><a href="/uri">link [foo [bar]]</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test503 is test do
+               var md = """[link \\[bar][ref]\n\n[ref]: /uri\n"""
+               var html = """<p><a href="/uri">link [bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test504 is test do
+               var md = """[link *foo **bar** `#`*][ref]\n\n[ref]: /uri\n"""
+               var html = """<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test505 is test do
+               var md = """[![moon](moon.jpg)][ref]\n\n[ref]: /uri\n"""
+               var html = """<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test506 is test do
+               var md = """[foo [bar](/uri)][ref]\n\n[ref]: /uri\n"""
+               var html = """<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test507 is test do
+               var md = """[foo *bar [baz][ref]*][ref]\n\n[ref]: /uri\n"""
+               var html = """<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test508 is test do
+               var md = """*[foo*][ref]\n\n[ref]: /uri\n"""
+               var html = """<p>*<a href="/uri">foo*</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test509 is test do
+               var md = """[foo *bar][ref]\n\n[ref]: /uri\n"""
+               var html = """<p><a href="/uri">foo *bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test510 is test do
+               var md = """[foo <bar attr="][ref]">\n\n[ref]: /uri\n"""
+               var html = """<p>[foo <bar attr="][ref]"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test511 is test do
+               var md = """[foo`][ref]`\n\n[ref]: /uri\n"""
+               var html = """<p>[foo<code>][ref]</code></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test512 is test do
+               var md = """[foo<http://example.com/?search=][ref]>\n\n[ref]: /uri\n"""
+               var html = """<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test513 is test do
+               var md = """[foo][BaR]\n\n[bar]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test515 is test do
+               var md = """[Foo\n  bar]: /url\n\n[Baz][Foo bar]\n"""
+               var html = """<p><a href="/url">Baz</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test516 is test do
+               var md = """[foo] [bar]\n\n[bar]: /url "title"\n"""
+               var html = """<p>[foo] <a href="/url" title="title">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test517 is test do
+               var md = """[foo]\n[bar]\n\n[bar]: /url "title"\n"""
+               var html = """<p>[foo]\n<a href="/url" title="title">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test518 is test do
+               var md = """[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]\n"""
+               var html = """<p><a href="/url1">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test519 is test do
+               var md = """[bar][foo\\!]\n\n[foo!]: /url\n"""
+               var html = """<p>[bar][foo!]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test520 is test do
+               var md = """[foo][ref[]\n\n[ref[]: /uri\n"""
+               var html = """<p>[foo][ref[]</p>\n<p>[ref[]: /uri</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test521 is test do
+               var md = """[foo][ref[bar]]\n\n[ref[bar]]: /uri\n"""
+               var html = """<p>[foo][ref[bar]]</p>\n<p>[ref[bar]]: /uri</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test522 is test do
+               var md = """[[[foo]]]\n\n[[[foo]]]: /url\n"""
+               var html = """<p>[[[foo]]]</p>\n<p>[[[foo]]]: /url</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test523 is test do
+               var md = """[foo][ref\\[]\n\n[ref\\[]: /uri\n"""
+               var html = """<p><a href="/uri">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test524 is test do
+               var md = """[bar\\\\]: /uri\n\n[bar\\\\]\n"""
+               var html = """<p><a href="/uri">bar\\</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test525 is test do
+               var md = """[]\n\n[]: /uri\n"""
+               var html = """<p>[]</p>\n<p>[]: /uri</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test526 is test do
+               var md = """[\n ]\n\n[\n ]: /uri\n"""
+               var html = """<p>[\n]</p>\n<p>[\n]: /uri</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test527 is test do
+               var md = """[foo][]\n\n[foo]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test528 is test do
+               var md = """[*foo* bar][]\n\n[*foo* bar]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title"><em>foo</em> bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test529 is test do
+               var md = """[Foo][]\n\n[foo]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">Foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test530 is test do
+               var md = """[foo] \n[]\n\n[foo]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">foo</a>\n[]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test531 is test do
+               var md = """[foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test532 is test do
+               var md = """[*foo* bar]\n\n[*foo* bar]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title"><em>foo</em> bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test533 is test do
+               var md = """[[*foo* bar]]\n\n[*foo* bar]: /url "title"\n"""
+               var html = """<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test534 is test do
+               var md = """[[bar [foo]\n\n[foo]: /url\n"""
+               var html = """<p>[[bar <a href="/url">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test535 is test do
+               var md = """[Foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p><a href="/url" title="title">Foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test536 is test do
+               var md = """[foo] bar\n\n[foo]: /url\n"""
+               var html = """<p><a href="/url">foo</a> bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test537 is test do
+               var md = """\\[foo]\n\n[foo]: /url "title"\n"""
+               var html = """<p>[foo]</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test538 is test do
+               var md = """[foo*]: /url\n\n*[foo*]\n"""
+               var html = """<p>*<a href="/url">foo*</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test539 is test do
+               var md = """[foo][bar]\n\n[foo]: /url1\n[bar]: /url2\n"""
+               var html = """<p><a href="/url2">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test540 is test do
+               var md = """[foo][]\n\n[foo]: /url1\n"""
+               var html = """<p><a href="/url1">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test541 is test do
+               var md = """[foo]()\n\n[foo]: /url1\n"""
+               var html = """<p><a href="">foo</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test542 is test do
+               var md = """[foo](not a link)\n\n[foo]: /url1\n"""
+               var html = """<p><a href="/url1">foo</a>(not a link)</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test543 is test do
+               var md = """[foo][bar][baz]\n\n[baz]: /url\n"""
+               var html = """<p>[foo]<a href="/url">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test544 is test do
+               var md = """[foo][bar][baz]\n\n[baz]: /url1\n[bar]: /url2\n"""
+               var html = """<p><a href="/url2">foo</a><a href="/url1">baz</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test545 is test do
+               var md = """[foo][bar][baz]\n\n[baz]: /url1\n[foo]: /url2\n"""
+               var html = """<p>[foo]<a href="/url1">bar</a></p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_list_items.nit b/lib/markdown2/tests/test_commonmark_list_items.nit
new file mode 100644 (file)
index 0000000..b4896ed
--- /dev/null
@@ -0,0 +1,310 @@
+# 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.
+
+module test_commonmark_list_items is test
+
+import test_markdown
+
+class TestCommonmarkListItems
+       super TestMarkdownHtml
+       test
+
+       fun test216 is test do
+               var md = """A paragraph\nwith two lines.\n\n    indented code\n\n> A block quote.\n"""
+               var html = """<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       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 html = """<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test218 is test do
+               var md = """- one\n\n two\n"""
+               var html = """<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test219 is test do
+               var md = """- one\n\n  two\n"""
+               var html = """<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test220 is test do
+               var md = """ -    one\n\n     two\n"""
+               var html = """<ul>\n<li>one</li>\n</ul>\n<pre><code> two\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test221 is test do
+               var md = """ -    one\n\n      two\n"""
+               var html = """<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test222 is test do
+               var md = """   > > 1.  one\n>>\n>>     two\n"""
+               var html = """<blockquote>\n<blockquote>\n<ol>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ol>\n</blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test223 is test do
+               var md = """>>- one\n>>\n  >  > two\n"""
+               var html = """<blockquote>\n<blockquote>\n<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n</blockquote>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test224 is test do
+               var md = """-one\n\n2.two\n"""
+               var html = """<p>-one</p>\n<p>2.two</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test225 is test do
+               var md = """- foo\n\n\n  bar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test226 is test do
+               var md = """1.  foo\n\n    ```\n    bar\n    ```\n\n    baz\n\n    > bam\n"""
+               var html = """<ol>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n<blockquote>\n<p>bam</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test227 is test do
+               var md = """- Foo\n\n      bar\n\n\n      baz\n"""
+               var html = """<ul>\n<li>\n<p>Foo</p>\n<pre><code>bar\n\n\nbaz\n</code></pre>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test228 is test do
+               var md = """123456789. ok\n"""
+               var html = """<ol start="123456789">\n<li>ok</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test229 is test do
+               var md = """1234567890. not ok\n"""
+               var html = """<p>1234567890. not ok</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test230 is test do
+               var md = """0. ok\n"""
+               var html = """<ol start="0">\n<li>ok</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test231 is test do
+               var md = """003. ok\n"""
+               var html = """<ol start="3">\n<li>ok</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test232 is test do
+               var md = """-1. not ok\n"""
+               var html = """<p>-1. not ok</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test233 is test do
+               var md = """- foo\n\n      bar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test234 is test do
+               var md = """  10.  foo\n\n           bar\n"""
+               var html = """<ol start="10">\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test235 is test do
+               var md = """    indented code\n\nparagraph\n\n    more code\n"""
+               var html = """<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test236 is test do
+               var md = """1.     indented code\n\n   paragraph\n\n       more code\n"""
+               var html = """<ol>\n<li>\n<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test237 is test do
+               var md = """1.      indented code\n\n   paragraph\n\n       more code\n"""
+               var html = """<ol>\n<li>\n<pre><code> indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test238 is test do
+               var md = """   foo\n\nbar\n"""
+               var html = """<p>foo</p>\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test239 is test do
+               var md = """-    foo\n\n  bar\n"""
+               var html = """<ul>\n<li>foo</li>\n</ul>\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test240 is test do
+               var md = """-  foo\n\n   bar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test241 is test do
+               var md = """-\n  foo\n-\n  ```\n  bar\n  ```\n-\n      baz\n"""
+               var html = """<ul>\n<li>foo</li>\n<li>\n<pre><code>bar\n</code></pre>\n</li>\n<li>\n<pre><code>baz\n</code></pre>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test242 is test do
+               var md = """-   \n  foo\n"""
+               var html = """<ul>\n<li>foo</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test243 is test do
+               var md = """-\n\n  foo\n"""
+               var html = """<ul>\n<li></li>\n</ul>\n<p>foo</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test244 is test do
+               var md = """- foo\n-\n- bar\n"""
+               var html = """<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test245 is test do
+               var md = """- foo\n-   \n- bar\n"""
+               var html = """<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test246 is test do
+               var md = """1. foo\n2.\n3. bar\n"""
+               var html = """<ol>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test247 is test do
+               var md = """*\n"""
+               var html = """<ul>\n<li></li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test248 is test do
+               var md = """foo\n*\n\nfoo\n1.\n"""
+               var html = """<p>foo\n*</p>\n<p>foo\n1.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test249 is test do
+               var md = """ 1.  A paragraph\n     with two lines.\n\n         indented code\n\n     > A block quote.\n"""
+               var html = """<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test250 is test do
+               var md = """  1.  A paragraph\n      with two lines.\n\n          indented code\n\n      > A block quote.\n"""
+               var html = """<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test251 is test do
+               var md = """   1.  A paragraph\n       with two lines.\n\n           indented code\n\n       > A block quote.\n"""
+               var html = """<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test252 is test do
+               var md = """    1.  A paragraph\n        with two lines.\n\n            indented code\n\n        > A block quote.\n"""
+               var html = """<pre><code>1.  A paragraph\n    with two lines.\n\n        indented code\n\n    &gt; A block quote.\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test253 is test do
+               var md = """  1.  A paragraph\nwith two lines.\n\n          indented code\n\n      > A block quote.\n"""
+               var html = """<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test254 is test do
+               var md = """  1.  A paragraph\n    with two lines.\n"""
+               var html = """<ol>\n<li>A paragraph\nwith two lines.</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test255 is test do
+               var md = """> 1. > Blockquote\ncontinued here.\n"""
+               var html = """<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test256 is test do
+               var md = """> 1. > Blockquote\n> continued here.\n"""
+               var html = """<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test257 is test do
+               var md = """- foo\n  - bar\n    - baz\n      - boo\n"""
+               var html = """<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz\n<ul>\n<li>boo</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test258 is test do
+               var md = """- foo\n - bar\n  - baz\n   - boo\n"""
+               var html = """<ul>\n<li>foo</li>\n<li>bar</li>\n<li>baz</li>\n<li>boo</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test259 is test do
+               var md = """10) foo\n    - bar\n"""
+               var html = """<ol start="10">\n<li>foo\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test260 is test do
+               var md = """10) foo\n   - bar\n"""
+               var html = """<ol start="10">\n<li>foo</li>\n</ol>\n<ul>\n<li>bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test261 is test do
+               var md = """- - foo\n"""
+               var html = """<ul>\n<li>\n<ul>\n<li>foo</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test262 is test do
+               var md = """1. - 2. foo\n"""
+               var html = """<ol>\n<li>\n<ul>\n<li>\n<ol start="2">\n<li>foo</li>\n</ol>\n</li>\n</ul>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test263 is test do
+               var md = """- # Foo\n- Bar\n  ---\n  baz\n"""
+               var html = """<ul>\n<li>\n<h1>Foo</h1>\n</li>\n<li>\n<h2>Bar</h2>\nbaz</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_lists.nit b/lib/markdown2/tests/test_commonmark_lists.nit
new file mode 100644 (file)
index 0000000..05ef4d4
--- /dev/null
@@ -0,0 +1,166 @@
+# 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.
+
+module test_commonmark_lists is test
+
+import test_markdown
+
+class TestCommonmarkLists
+       super TestMarkdownHtml
+       test
+
+       fun test264 is test do
+               var md = """- foo\n- bar\n+ baz\n"""
+               var html = """<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<ul>\n<li>baz</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test265 is test do
+               var md = """1. foo\n2. bar\n3) baz\n"""
+               var html = """<ol>\n<li>foo</li>\n<li>bar</li>\n</ol>\n<ol start="3">\n<li>baz</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test266 is test do
+               var md = """Foo\n- bar\n- baz\n"""
+               var html = """<p>Foo</p>\n<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test267 is test do
+               var md = """The number of windows in my house is\n14.  The number of doors is 6.\n"""
+               var html = """<p>The number of windows in my house is\n14.  The number of doors is 6.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test268 is test do
+               var md = """The number of windows in my house is\n1.  The number of doors is 6.\n"""
+               var html = """<p>The number of windows in my house is</p>\n<ol>\n<li>The number of doors is 6.</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test269 is test do
+               var md = """- foo\n\n- bar\n\n\n- baz\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n</li>\n<li>\n<p>bar</p>\n</li>\n<li>\n<p>baz</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test270 is test do
+               var md = """- foo\n  - bar\n    - baz\n\n\n      bim\n"""
+               var html = """<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>\n<p>baz</p>\n<p>bim</p>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test271 is test do
+               var md = """- foo\n- bar\n\n<!-- -->\n\n- baz\n- bim\n"""
+               var html = """<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<!-- -->\n<ul>\n<li>baz</li>\n<li>bim</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test272 is test do
+               var md = """-   foo\n\n    notcode\n\n-   foo\n\n<!-- -->\n\n    code\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<p>notcode</p>\n</li>\n<li>\n<p>foo</p>\n</li>\n</ul>\n<!-- -->\n<pre><code>code\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test273 is test do
+               var md = """- a\n - b\n  - c\n   - d\n  - e\n - f\n- g\n"""
+               var html = """<ul>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n<li>d</li>\n<li>e</li>\n<li>f</li>\n<li>g</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test274 is test do
+               var md = """1. a\n\n  2. b\n\n   3. c\n"""
+               var html = """<ol>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test277 is test do
+               var md = """- a\n- b\n\n- c\n"""
+               var html = """<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test278 is test do
+               var md = """* a\n*\n\n* c\n"""
+               var html = """<ul>\n<li>\n<p>a</p>\n</li>\n<li></li>\n<li>\n<p>c</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test279 is test do
+               var md = """- a\n- b\n\n  c\n- d\n"""
+               var html = """<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test280 is test do
+               var md = """- a\n- b\n\n  [ref]: /url\n- d\n"""
+               var html = """<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test281 is test do
+               var md = """- a\n- ```\n  b\n\n\n  ```\n- c\n"""
+               var html = """<ul>\n<li>a</li>\n<li>\n<pre><code>b\n\n\n</code></pre>\n</li>\n<li>c</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test282 is test do
+               var md = """- a\n  - b\n\n    c\n- d\n"""
+               var html = """<ul>\n<li>a\n<ul>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n</ul>\n</li>\n<li>d</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test283 is test do
+               var md = """* a\n  > b\n  >\n* c\n"""
+               var html = """<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n</li>\n<li>c</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test284 is test do
+               var md = """- a\n  > b\n  ```\n  c\n  ```\n- d\n"""
+               var html = """<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n<pre><code>c\n</code></pre>\n</li>\n<li>d</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test285 is test do
+               var md = """- a\n"""
+               var html = """<ul>\n<li>a</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test286 is test do
+               var md = """- a\n  - b\n"""
+               var html = """<ul>\n<li>a\n<ul>\n<li>b</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test287 is test do
+               var md = """1. ```\n   foo\n   ```\n\n   bar\n"""
+               var html = """<ol>\n<li>\n<pre><code>foo\n</code></pre>\n<p>bar</p>\n</li>\n</ol>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test288 is test do
+               var md = """* foo\n  * bar\n\n  baz\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n<p>baz</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test289 is test do
+               var md = """- a\n  - b\n  - c\n\n- d\n  - e\n  - f\n"""
+               var html = """<ul>\n<li>\n<p>a</p>\n<ul>\n<li>b</li>\n<li>c</li>\n</ul>\n</li>\n<li>\n<p>d</p>\n<ul>\n<li>e</li>\n<li>f</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_paragraphs.nit b/lib/markdown2/tests/test_commonmark_paragraphs.nit
new file mode 100644 (file)
index 0000000..6501c95
--- /dev/null
@@ -0,0 +1,70 @@
+# 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.
+
+module test_commonmark_paragraphs is test
+
+import test_markdown
+
+class TestCommonmarkParagraphs
+       super TestMarkdownHtml
+       test
+
+       fun test182 is test do
+               var md = """aaa\n\nbbb\n"""
+               var html = """<p>aaa</p>\n<p>bbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test183 is test do
+               var md = """aaa\nbbb\n\nccc\nddd\n"""
+               var html = """<p>aaa\nbbb</p>\n<p>ccc\nddd</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test184 is test do
+               var md = """aaa\n\n\nbbb\n"""
+               var html = """<p>aaa</p>\n<p>bbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test185 is test do
+               var md = """  aaa\n bbb\n"""
+               var html = """<p>aaa\nbbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test186 is test do
+               var md = """aaa\n             bbb\n                                       ccc\n"""
+               var html = """<p>aaa\nbbb\nccc</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test187 is test do
+               var md = """   aaa\nbbb\n"""
+               var html = """<p>aaa\nbbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test188 is test do
+               var md = """    aaa\nbbb\n"""
+               var html = """<pre><code>aaa\n</code></pre>\n<p>bbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test189 is test do
+               var md = """aaa     \nbbb     \n"""
+               var html = """<p>aaa<br />\nbbb</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_precedence.nit b/lib/markdown2/tests/test_commonmark_precedence.nit
new file mode 100644 (file)
index 0000000..a9938c0
--- /dev/null
@@ -0,0 +1,28 @@
+# 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.
+
+module test_commonmark_precedence is test
+
+import test_markdown
+
+class TestCommonmarkPrecedence
+       super TestMarkdownHtml
+       test
+
+       fun test12 is test do
+               var md = """- `one\n- two`\n"""
+               var html = """<ul>\n<li>`one</li>\n<li>two`</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_raw_html.nit b/lib/markdown2/tests/test_commonmark_raw_html.nit
new file mode 100644 (file)
index 0000000..9e05a46
--- /dev/null
@@ -0,0 +1,148 @@
+# 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.
+
+module test_commonmark_raw_html is test
+
+import test_markdown
+
+class TestCommonmarkRawHTML
+       super TestMarkdownHtml
+       test
+
+       fun test587 is test do
+               var md = """<a><bab><c2c>\n"""
+               var html = """<p><a><bab><c2c></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test588 is test do
+               var md = """<a/><b2/>\n"""
+               var html = """<p><a/><b2/></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test589 is test do
+               var md = """<a  /><b2\ndata="foo" >\n"""
+               var html = """<p><a  /><b2\ndata="foo" ></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test590 is test do
+               var md = """<a foo="bar" bam = 'baz <em>"</em>'\n_boolean zoop:33=zoop:33 />\n"""
+               var html = """<p><a foo="bar" bam = 'baz <em>"</em>'\n_boolean zoop:33=zoop:33 /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test591 is test do
+               var md = """Foo <responsive-image src="foo.jpg" />\n"""
+               var html = """<p>Foo <responsive-image src="foo.jpg" /></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test592 is test do
+               var md = """<33> <__>\n"""
+               var html = """<p>&lt;33&gt; &lt;__&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test593 is test do
+               var md = """<a h*#ref="hi">\n"""
+               var html = """<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test594 is test do
+               var md = """<a href="hi'> <a href=hi'>\n"""
+               var html = """<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test595 is test do
+               var md = """< a><\nfoo><bar/ >\n<foo bar=baz\nbim!bop />\n"""
+               var html = """<p>&lt; a&gt;&lt;\nfoo&gt;&lt;bar/ &gt;\n&lt;foo bar=baz\nbim!bop /&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test596 is test do
+               var md = """<a href='bar'title=title>\n"""
+               var html = """<p>&lt;a href='bar'title=title&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test597 is test do
+               var md = """</a></foo >\n"""
+               var html = """<p></a></foo ></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test598 is test do
+               var md = """</a href="foo">\n"""
+               var html = """<p>&lt;/a href=&quot;foo&quot;&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test599 is test do
+               var md = """foo <!-- this is a\ncomment - with hyphen -->\n"""
+               var html = """<p>foo <!-- this is a\ncomment - with hyphen --></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test600 is test do
+               var md = """foo <!-- not a comment -- two hyphens -->\n"""
+               var html = """<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test601 is test do
+               var md = """foo <!--> foo -->\n\nfoo <!-- foo--->\n"""
+               var html = """<p>foo &lt;!--&gt; foo --&gt;</p>\n<p>foo &lt;!-- foo---&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test602 is test do
+               var md = """foo <?php echo $a; ?>\n"""
+               var html = """<p>foo <?php echo $a; ?></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test603 is test do
+               var md = """foo <!ELEMENT br EMPTY>\n"""
+               var html = """<p>foo <!ELEMENT br EMPTY></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test604 is test do
+               var md = """foo <![CDATA[>&<]]>\n"""
+               var html = """<p>foo <![CDATA[>&<]]></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test605 is test do
+               var md = """foo <a href="&ouml;">\n"""
+               var html = """<p>foo <a href="&ouml;"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test606 is test do
+               var md = """foo <a href="\\*">\n"""
+               var html = """<p>foo <a href="\\*"></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test607 is test do
+               var md = """<a href="\\"">\n"""
+               var html = """<p>&lt;a href=&quot;&quot;&quot;&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_setext_headings.nit b/lib/markdown2/tests/test_commonmark_setext_headings.nit
new file mode 100644 (file)
index 0000000..c92f4d0
--- /dev/null
@@ -0,0 +1,178 @@
+# 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.
+
+module test_commonmark_setext_headings is test
+
+import test_markdown
+
+class TestCommonmarkSetextHeadings
+       super TestMarkdownHtml
+       test
+
+       fun test50 is test do
+               var md = """Foo *bar*\n=========\n\nFoo *bar*\n---------\n"""
+               var html = """<h1>Foo <em>bar</em></h1>\n<h2>Foo <em>bar</em></h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test51 is test do
+               var md = """Foo *bar\nbaz*\n====\n"""
+               var html = """<h1>Foo <em>bar\nbaz</em></h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test52 is test do
+               var md = """Foo\n-------------------------\n\nFoo\n=\n"""
+               var html = """<h2>Foo</h2>\n<h1>Foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test53 is test do
+               var md = """   Foo\n---\n\n  Foo\n-----\n\n  Foo\n  ===\n"""
+               var html = """<h2>Foo</h2>\n<h2>Foo</h2>\n<h1>Foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test54 is test do
+               var md = """    Foo\n    ---\n\n    Foo\n---\n"""
+               var html = """<pre><code>Foo\n---\n\nFoo\n</code></pre>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test55 is test do
+               var md = """Foo\n   ----      \n"""
+               var html = """<h2>Foo</h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test56 is test do
+               var md = """Foo\n    ---\n"""
+               var html = """<p>Foo\n---</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test57 is test do
+               var md = """Foo\n= =\n\nFoo\n--- -\n"""
+               var html = """<p>Foo\n= =</p>\n<p>Foo</p>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test58 is test do
+               var md = """Foo  \n-----\n"""
+               var html = """<h2>Foo</h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test59 is test do
+               var md = """Foo\\\n----\n"""
+               var html = """<h2>Foo\\</h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test60 is test do
+               var md = """`Foo\n----\n`\n\n<a title="a lot\n---\nof dashes"/>\n"""
+               var html = """<h2>`Foo</h2>\n<p>`</p>\n<h2>&lt;a title=&quot;a lot</h2>\n<p>of dashes&quot;/&gt;</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test61 is test do
+               var md = """> Foo\n---\n"""
+               var html = """<blockquote>\n<p>Foo</p>\n</blockquote>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test62 is test do
+               var md = """> foo\nbar\n===\n"""
+               var html = """<blockquote>\n<p>foo\nbar\n===</p>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test63 is test do
+               var md = """- Foo\n---\n"""
+               var html = """<ul>\n<li>Foo</li>\n</ul>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test64 is test do
+               var md = """Foo\nBar\n---\n"""
+               var html = """<h2>Foo\nBar</h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test65 is test do
+               var md = """---\nFoo\n---\nBar\n---\nBaz\n"""
+               var html = """<hr />\n<h2>Foo</h2>\n<h2>Bar</h2>\n<p>Baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test66 is test do
+               var md = """\n====\n"""
+               var html = """<p>====</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test67 is test do
+               var md = """---\n---\n"""
+               var html = """<hr />\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test68 is test do
+               var md = """- foo\n-----\n"""
+               var html = """<ul>\n<li>foo</li>\n</ul>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test69 is test do
+               var md = """    foo\n---\n"""
+               var html = """<pre><code>foo\n</code></pre>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test70 is test do
+               var md = """> foo\n-----\n"""
+               var html = """<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test71 is test do
+               var md = """\\> foo\n------\n"""
+               var html = """<h2>&gt; foo</h2>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test72 is test do
+               var md = """Foo\n\nbar\n---\nbaz\n"""
+               var html = """<p>Foo</p>\n<h2>bar</h2>\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test73 is test do
+               var md = """Foo\nbar\n\n---\n\nbaz\n"""
+               var html = """<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test74 is test do
+               var md = """Foo\nbar\n* * *\nbaz\n"""
+               var html = """<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test75 is test do
+               var md = """Foo\nbar\n\\---\nbaz\n"""
+               var html = """<p>Foo\nbar\n---\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_soft_line_breaks.nit b/lib/markdown2/tests/test_commonmark_soft_line_breaks.nit
new file mode 100644 (file)
index 0000000..c3ce0f1
--- /dev/null
@@ -0,0 +1,34 @@
+# 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.
+
+module test_commonmark_soft_line_breaks is test
+
+import test_markdown
+
+class TestCommonmarkSoftLineBreaks
+       super TestMarkdownHtml
+       test
+
+       fun test623 is test do
+               var md = """foo\nbaz\n"""
+               var html = """<p>foo\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test624 is test do
+               var md = """foo \n baz\n"""
+               var html = """<p>foo\nbaz</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_tabs.nit b/lib/markdown2/tests/test_commonmark_tabs.nit
new file mode 100644 (file)
index 0000000..2a2f2ca
--- /dev/null
@@ -0,0 +1,88 @@
+# 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.
+
+module test_commonmark_tabs is test
+
+import test_markdown
+
+class TestCommonmarkTabs
+       super TestMarkdownHtml
+       test
+
+       fun test1 is test do
+               var md = """\tfoo\tbaz\t\tbim\n"""
+               var html = """<pre><code>foo\tbaz\t\tbim\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test2 is test do
+               var md = """  \tfoo\tbaz\t\tbim\n"""
+               var html = """<pre><code>foo\tbaz\t\tbim\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test3 is test do
+               var md = """    a\ta\n    ὐ\ta\n"""
+               var html = """<pre><code>a\ta\nὐ\ta\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test4 is test do
+               var md = """  - foo\n\n\tbar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test5 is test do
+               var md = """- foo\n\n\t\tbar\n"""
+               var html = """<ul>\n<li>\n<p>foo</p>\n<pre><code>  bar\n</code></pre>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test6 is test do
+               var md = """>\t\tfoo\n"""
+               var html = """<blockquote>\n<pre><code>  foo\n</code></pre>\n</blockquote>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test7 is test do
+               var md = """-\t\tfoo\n"""
+               var html = """<ul>\n<li>\n<pre><code>  foo\n</code></pre>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test8 is test do
+               var md = """    foo\n\tbar\n"""
+               var html = """<pre><code>foo\nbar\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test9 is test do
+               var md = """ - foo\n   - bar\n\t - baz\n"""
+               var html = """<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test10 is test do
+               var md = """#\tFoo\n"""
+               var html = """<h1>Foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test11 is test do
+               var md = """*\t*\t*\t\n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_textual_content.nit b/lib/markdown2/tests/test_commonmark_textual_content.nit
new file mode 100644 (file)
index 0000000..235e20e
--- /dev/null
@@ -0,0 +1,40 @@
+# 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.
+
+module test_commonmark_textual_content is test
+
+import test_markdown
+
+class TestCommonmarkTextualContent
+       super TestMarkdownHtml
+       test
+
+       fun test625 is test do
+               var md = """hello $.;'there\n"""
+               var html = """<p>hello $.;'there</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test626 is test do
+               var md = """Foo χρῆν\n"""
+               var html = """<p>Foo χρῆν</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test627 is test do
+               var md = """Multiple     spaces\n"""
+               var html = """<p>Multiple     spaces</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_commonmark_thematic_breaks.nit b/lib/markdown2/tests/test_commonmark_thematic_breaks.nit
new file mode 100644 (file)
index 0000000..ae68fa4
--- /dev/null
@@ -0,0 +1,136 @@
+# 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.
+
+module test_commonmark_thematic_breaks is test
+
+import test_markdown
+
+class TestCommonmarkThematicBreaks
+       super TestMarkdownHtml
+       test
+
+       fun test13 is test do
+               var md = """***\n---\n___\n"""
+               var html = """<hr />\n<hr />\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test14 is test do
+               var md = """+++\n"""
+               var html = """<p>+++</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test15 is test do
+               var md = """===\n"""
+               var html = """<p>===</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test16 is test do
+               var md = """--\n**\n__\n"""
+               var html = """<p>--\n**\n__</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test17 is test do
+               var md = """ ***\n  ***\n   ***\n"""
+               var html = """<hr />\n<hr />\n<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test18 is test do
+               var md = """    ***\n"""
+               var html = """<pre><code>***\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test19 is test do
+               var md = """Foo\n    ***\n"""
+               var html = """<p>Foo\n***</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test20 is test do
+               var md = """_____________________________________\n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test21 is test do
+               var md = """ - - -\n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test22 is test do
+               var md = """ **  * ** * ** * **\n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test23 is test do
+               var md = """-     -      -      -\n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test24 is test do
+               var md = """- - - -    \n"""
+               var html = """<hr />\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test25 is test do
+               var md = """_ _ _ _ a\n\na------\n\n---a---\n"""
+               var html = """<p>_ _ _ _ a</p>\n<p>a------</p>\n<p>---a---</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test26 is test do
+               var md = """ *-*\n"""
+               var html = """<p><em>-</em></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test27 is test do
+               var md = """- foo\n***\n- bar\n"""
+               var html = """<ul>\n<li>foo</li>\n</ul>\n<hr />\n<ul>\n<li>bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test28 is test do
+               var md = """Foo\n***\nbar\n"""
+               var html = """<p>Foo</p>\n<hr />\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test29 is test do
+               var md = """Foo\n---\nbar\n"""
+               var html = """<h2>Foo</h2>\n<p>bar</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test30 is test do
+               var md = """* Foo\n* * *\n* Bar\n"""
+               var html = """<ul>\n<li>Foo</li>\n</ul>\n<hr />\n<ul>\n<li>Bar</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test31 is test do
+               var md = """- Foo\n- * * *\n"""
+               var html = """<ul>\n<li>Foo</li>\n<li>\n<hr />\n</li>\n</ul>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown.nit b/lib/markdown2/tests/test_markdown.nit
new file mode 100644 (file)
index 0000000..60f1f34
--- /dev/null
@@ -0,0 +1,42 @@
+# 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.
+
+# Test suites for module `markdown`
+module test_markdown
+
+import markdown_html_rendering
+
+# Abstract test class that instanciates the markdown parser
+abstract class TestMarkdown
+
+       # Markdown parser to use in tests
+       var md_parser = new MdParser
+
+       # Parse a `md` string as a MdNode
+       fun parse_md(md: String): MdNode do return md_parser.parse(md)
+end
+
+# Abstract test class that defines the test methods for HTML rendering
+abstract class TestMarkdownHtml
+       super TestMarkdown
+
+       # HTML renderer used in tests
+       var html_renderer = new HtmlRenderer
+
+       # Render the `md` string as HTML
+       fun md_to_html(md: String): String do
+               var node = parse_md(md)
+               return html_renderer.render(node)
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_blocks.nit b/lib/markdown2/tests/test_markdown_blocks.nit
new file mode 100644 (file)
index 0000000..f81d225
--- /dev/null
@@ -0,0 +1,660 @@
+# 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 htmlress or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Test for markdown blocks parsing
+module test_markdown_blocks is test
+
+import test_markdown
+
+class TestMarkdownBlocks
+       super TestMarkdownHtml
+       test
+
+       fun test_blocks_empty is test do
+               var md = ""
+               var html = ""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_tabs is test do
+               var md = """\tsome code\n"""
+               var html = """<pre><code>some code\n</code></pre>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_pagraph1 is test do
+               var md = "test\n"
+               var html = "<p>test</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_pagraph2 is test do
+               var md = """line1\nline2\n\nline3 line4\n\nline5\n"""
+               var html = """<p>line1\nline2</p>\n<p>line3 line4</p>\n<p>line5</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_pagraph3 is test do
+               var md = """
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """
+<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
+<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_headings_1 is test do
+               var md = """
+This is a H1
+=============
+
+This is a H2
+-------------
+"""
+               var html = """
+<h1>This is a H1</h1>
+<h2>This is a H2</h2>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_headings_2 is test do
+               var md = """
+# This is a H1
+
+## This is a H2
+###### This is a H6
+"""
+               var html = """
+<h1>This is a H1</h1>
+<h2>This is a H2</h2>
+<h6>This is a H6</h6>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_headings_3 is test do
+               var md = """
+# This is a H1 #
+
+## This is a H2 ##
+
+### This is a H3 ######
+"""
+               var html = """
+<h1>This is a H1</h1>
+<h2>This is a H2</h2>
+<h3>This is a H3</h3>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_hr1 is test do
+               var md = """
+* * *
+
+***
+
+*****
+
+- - -
+
+---------------------------------------
+"""
+               var html = "<hr />\n<hr />\n<hr />\n<hr />\n<hr />\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_bquote1 is test do
+               var md = """
+> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+>
+> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+> id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """<blockquote>
+<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
+<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.</p>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_bquote2 is test do
+               var md = """
+> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """<blockquote>
+<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
+</blockquote>
+<blockquote>
+<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.</p>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_bquote3 is test do
+               var md = """
+> This is the first level of quoting.
+>
+> > This is nested blockquote.
+>
+> Back to the first level.
+"""
+               var html = """<blockquote>
+<p>This is the first level of quoting.</p>
+<blockquote>
+<p>This is nested blockquote.</p>
+</blockquote>
+<p>Back to the first level.</p>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list1 is test do
+               var md = """
+*   Red
+*   Green
+*   Blue
+"""
+               var html = """<ul>
+<li>Red</li>
+<li>Green</li>
+<li>Blue</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list2 is test do
+               var md = """
++   Red
++   Green
++   Blue
+"""
+               var html = """<ul>
+<li>Red</li>
+<li>Green</li>
+<li>Blue</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list3 is test do
+               var md = """
+-   Red
+-   Green
+-   Blue
+"""
+               var html = """<ul>
+<li>Red</li>
+<li>Green</li>
+<li>Blue</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list4 is test do
+               var md = """
+1.  Bird
+2.  McHale
+3.  Parish
+"""
+               var html = """<ol>
+<li>Bird</li>
+<li>McHale</li>
+<li>Parish</li>
+</ol>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list5 is test do
+               var md = """
+3. Bird
+1. McHale
+8. Parish
+"""
+               var html = """<ol start=\"3\">
+<li>Bird</li>
+<li>McHale</li>
+<li>Parish</li>
+</ol>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list6 is test do
+               var md = """
+*   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+    viverra nec, fringilla in, laoreet vitae, risus.
+*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+    Suspendisse id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """
+<ul>
+<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.</li>
+<li>Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list7 is test do
+               var md = """
+*   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.
+*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """
+<ul>
+<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.</li>
+<li>Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list8 is test do
+               var md = """
+*   Bird
+
+*   Magic
+"""
+               var html = """
+<ul>
+<li>
+<p>Bird</p>
+</li>
+<li>
+<p>Magic</p>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list9 is test do
+               var md = """
+1.  This is a list item with two paragraphs. Lorem ipsum dolor
+    sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+    mi posuere lectus.
+
+    Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+    vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+    sit amet velit.
+
+2.  Suspendisse id sem consectetuer libero luctus adipiscing.
+"""
+               var html = """
+<ol>
+<li>
+<p>This is a list item with two paragraphs. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+mi posuere lectus.</p>
+<p>Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+sit amet velit.</p>
+</li>
+<li>
+<p>Suspendisse id sem consectetuer libero luctus adipiscing.</p>
+</li>
+</ol>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list10 is test do
+               var md = """
+*   This is a list item with two paragraphs.
+
+    This is the second paragraph in the list item. You're
+only required to indent the first line. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit.
+
+*   Another item in the same list.
+"""
+               var html = """
+<ul>
+<li>
+<p>This is a list item with two paragraphs.</p>
+<p>This is the second paragraph in the list item. You're
+only required to indent the first line. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit.</p>
+</li>
+<li>
+<p>Another item in the same list.</p>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_list_ext is test do
+               var md = """
+This is a paragraph
+* and this is a list
+"""
+               var html = """
+<p>This is a paragraph</p>
+<ul>
+<li>and this is a list</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_indented_code1 is test do
+               var md = """
+This is a normal paragraph:
+
+    This is a code block.
+"""
+               var html = """<p>This is a normal paragraph:</p>
+<pre><code>This is a code block.
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_indented_code2 is test do
+               var md = """
+Here is an example of AppleScript:
+
+    tell application "Foo"
+        beep
+    end tell
+
+    <div class="footer">
+        &copy; 2004 Foo Corporation
+    </div>
+"""
+               var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application &quot;Foo&quot;
+    beep
+end tell
+
+&lt;div class=&quot;footer&quot;&gt;
+    &amp;copy; 2004 Foo Corporation
+&lt;/div&gt;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_indented_code3 is test do
+               var md = """
+Here is an example of AppleScript:
+
+    beep
+"""
+               var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>beep
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_fenced_code1 is test do
+               var md = """
+Here is an example of AppleScript:
+~~~
+tell application "Foo"
+    beep
+end tell
+
+<div class="footer">
+    &copy; 2004 Foo Corporation
+</div>
+~~~
+"""
+               var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application &quot;Foo&quot;
+    beep
+end tell
+
+&lt;div class=&quot;footer&quot;&gt;
+    &amp;copy; 2004 Foo Corporation
+&lt;/div&gt;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_fenced_code2 is test do
+               var md = """
+Here is an example of AppleScript:
+```
+tell application "Foo"
+    beep
+end tell
+
+<div class="footer">
+    &copy; 2004 Foo Corporation
+</div>
+```
+"""
+               var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application &quot;Foo&quot;
+    beep
+end tell
+
+&lt;div class=&quot;footer&quot;&gt;
+    &amp;copy; 2004 Foo Corporation
+&lt;/div&gt;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_fenced_code3 is test do
+               var md = """
+```nit
+print "Hello World!"
+```
+"""
+               var html = """
+<pre><code class="language-nit">print &quot;Hello World!&quot;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_fenced_code4 is test do
+               var md = """
+~~~
+print "Hello"
+~~~
+~~~
+print "World"
+~~~
+"""
+               var html = """
+<pre><code>print &quot;Hello&quot;
+</code></pre>
+<pre><code>print &quot;World&quot;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_fenced_code5 is test do
+               var md = """
+~~~
+print "Hello"
+~~~
+~~~
+print "World"
+~~~
+"""
+               var html = """
+<pre><code>print &quot;Hello&quot;
+</code></pre>
+<pre><code>print &quot;World&quot;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_nesting1 is test do
+               var md = """
+> ## This is a header.
+>
+> 1.   This is the first list item.
+> 2.   This is the second list item.
+>
+> Here's some example code:
+>
+>     return shell_exec("echo $input | $markdown_script");
+"""
+               var html = """
+<blockquote>
+<h2>This is a header.</h2>
+<ol>
+<li>This is the first list item.</li>
+<li>This is the second list item.</li>
+</ol>
+<p>Here's some example code:</p>
+<pre><code>return shell_exec(&quot;echo $input | $markdown_script&quot;);
+</code></pre>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_nesting2 is test do
+               var md = """
+*   A list item with a blockquote:
+
+    > This is a blockquote
+    > inside a list item.
+"""
+               var html = """
+<ul>
+<li>
+<p>A list item with a blockquote:</p>
+<blockquote>
+<p>This is a blockquote
+inside a list item.</p>
+</blockquote>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_nesting3 is test do
+               var md = """
+*   A list item with a code block:
+
+        <code goes here>
+"""
+               var html = """
+<ul>
+<li>
+<p>A list item with a code block:</p>
+<pre><code>&lt;code goes here&gt;
+</code></pre>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_nesting4 is test do
+               var md = """
+*      Tab
+       *       Tab
+               *       Tab
+"""
+               var html = """
+<ul>
+<li>Tab
+<ul>
+<li>Tab
+<ul>
+<li>Tab</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_blocks_nesting5 is test do
+                       var md = """
+*      this
+
+       *       sub
+
+               that
+"""
+                       var html = """
+<ul>
+<li>
+<p>this</p>
+<ul>
+<li>
+<p>sub</p>
+<p>that</p>
+</li>
+</ul>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_daring.nit b/lib/markdown2/tests/test_markdown_daring.nit
new file mode 100644 (file)
index 0000000..962e356
--- /dev/null
@@ -0,0 +1,1343 @@
+# 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 htmlress or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Markdown tests from DaringFireball
+#
+# See <https://daringfireball.net/projects/markdown/>.
+module test_markdown_daring is test
+
+import test_markdown
+
+class TestMarkdownDaring
+       super TestMarkdownHtml
+       test
+
+       fun test_daring_encoding is test do
+               var md = """
+AT&T has an ampersand in their name.
+
+AT&amp;T is another way to write it.
+
+This & that.
+
+4 < 5.
+
+6 > 5.
+
+Here's a [link][1] with an ampersand in the URL.
+
+Here's a link with an ampersand in the link text: [AT&T][2].
+
+Here's an inline [link](/script?foo=1&bar=2).
+
+Here's an inline [link](</script?foo=1&bar=2>).
+
+
+[1]: http://example.com/?foo=1&bar=2
+[2]: http://att.com/  "AT&T"
+"""
+
+               var html = """
+<p>AT&amp;T has an ampersand in their name.</p>
+<p>AT&amp;T is another way to write it.</p>
+<p>This &amp; that.</p>
+<p>4 &lt; 5.</p>
+<p>6 &gt; 5.</p>
+<p>Here's a <a href="http://example.com/?foo=1&amp;bar=2">link</a> with an ampersand in the URL.</p>
+<p>Here's a link with an ampersand in the link text: <a href="http://att.com/" title="AT&amp;T">AT&amp;T</a>.</p>
+<p>Here's an inline <a href="/script?foo=1&amp;bar=2">link</a>.</p>
+<p>Here's an inline <a href="/script?foo=1&amp;bar=2">link</a>.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_autolinks is test do
+               var md = """
+Link: <http://example.com/>.
+
+With an ampersand: <http://example.com/?foo=1&bar=2>
+
+* In a list?
+* <http://example.com/>
+* It should.
+
+> Blockquoted: <http://example.com/>
+
+Auto-links should not occur here: `<http://example.com/>`
+
+       or here: <http://example.com/>
+"""
+
+               var html = """
+<p>Link: <a href="http://example.com/">http://example.com/</a>.</p>
+<p>With an ampersand: <a href="http://example.com/?foo=1&amp;bar=2">http://example.com/?foo=1&amp;bar=2</a></p>
+<ul>
+<li>In a list?</li>
+<li><a href="http://example.com/">http://example.com/</a></li>
+<li>It should.</li>
+</ul>
+<blockquote>
+<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>
+</blockquote>
+<p>Auto-links should not occur here: <code>&lt;http://example.com/&gt;</code></p>
+<pre><code>or here: &lt;http://example.com/&gt;
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_escape is test do
+               var md = """
+These should all get escaped:
+
+Backslash: \\
+
+Backtick: \`
+
+Asterisk: \*
+
+Underscore: \_
+
+Left brace: \{
+
+Right brace: \}
+
+Left bracket: \[
+
+Right bracket: \]
+
+Left paren: \(
+
+Right paren: \)
+
+Greater-than: \>
+
+Hash: \#
+
+Period: \.
+
+Bang: \!
+
+Plus: \+
+
+Minus: \-
+
+
+These should not, because they occur within a code block:
+
+       Backslash: \\
+
+       Backtick: \`
+
+       Asterisk: \*
+
+       Underscore: \_
+
+       Left brace: \{
+
+       Right brace: \}
+
+       Left bracket: \[
+
+       Right bracket: \]
+
+       Left paren: \(
+
+       Right paren: \)
+
+       Greater-than: \>
+
+       Hash: \#
+
+       Period: \.
+
+       Bang: \!
+
+       Plus: \+
+
+       Minus: \-
+
+Nor should these, which occur in code spans:
+
+Backslash: `\\`
+
+Backtick: `` \` ``
+
+Asterisk: `\*`
+
+Underscore: `\_`
+
+Left brace: `\{`
+
+Right brace: `\}`
+
+Left bracket: `\[`
+
+Right bracket: `\]`
+
+Left paren: `\(`
+
+Right paren: `\)`
+
+Greater-than: `\>`
+
+Hash: `\#`
+
+Period: `\.`
+
+Bang: `\!`
+
+Plus: `\+`
+
+Minus: `\-`
+
+These should get escaped, even though they're matching pairs for
+other Markdown constructs:
+
+\\\*asterisks\\\*
+
+\\\_underscores\\\_
+
+\\\`backticks\\\`
+
+This is a code span with a literal backslash-backtick sequence: `` \` ``
+
+This is a tag with unescaped backticks <span attr='`ticks`'>bar</span>.
+
+This is a tag with backslashes <span attr='\\\\backslashes\\\\'>bar</span>.
+"""
+               var html = """
+<p>These should all get escaped:</p>
+<p>Backslash: \\</p>
+<p>Backtick: \`</p>
+<p>Asterisk: \*</p>
+<p>Underscore: \_</p>
+<p>Left brace: \{</p>
+<p>Right brace: \}</p>
+<p>Left bracket: \[</p>
+<p>Right bracket: \]</p>
+<p>Left paren: \(</p>
+<p>Right paren: \)</p>
+<p>Greater-than: &gt;</p>
+<p>Hash: \#</p>
+<p>Period: \.</p>
+<p>Bang: \!</p>
+<p>Plus: \+</p>
+<p>Minus: \-</p>
+<p>These should not, because they occur within a code block:</p>
+<pre><code>Backslash: \\
+
+Backtick: \`
+
+Asterisk: \*
+
+Underscore: \_
+
+Left brace: \{
+
+Right brace: \}
+
+Left bracket: \[
+
+Right bracket: \]
+
+Left paren: \(
+
+Right paren: \)
+
+Greater-than: \&gt;
+
+Hash: \#
+
+Period: \.
+
+Bang: \!
+
+Plus: \+
+
+Minus: \-
+</code></pre>
+<p>Nor should these, which occur in code spans:</p>
+<p>Backslash: <code>\\</code></p>
+<p>Backtick: <code>\`</code></p>
+<p>Asterisk: <code>\*</code></p>
+<p>Underscore: <code>\_</code></p>
+<p>Left brace: <code>\{</code></p>
+<p>Right brace: <code>\}</code></p>
+<p>Left bracket: <code>\[</code></p>
+<p>Right bracket: <code>\]</code></p>
+<p>Left paren: <code>\(</code></p>
+<p>Right paren: <code>\)</code></p>
+<p>Greater-than: <code>\&gt;</code></p>
+<p>Hash: <code>\#</code></p>
+<p>Period: <code>\.</code></p>
+<p>Bang: <code>\!</code></p>
+<p>Plus: <code>\+</code></p>
+<p>Minus: <code>\-</code></p>
+<p>These should get escaped, even though they're matching pairs for
+other Markdown constructs:</p>
+<p>*asterisks*</p>
+<p>_underscores_</p>
+<p>`backticks`</p>
+<p>This is a code span with a literal backslash-backtick sequence: <code>\`</code></p>
+<p>This is a tag with unescaped backticks <span attr='`ticks`'>bar</span>.</p>
+<p>This is a tag with backslashes <span attr='\\\\backslashes\\\\'>bar</span>.</p>
+"""
+
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_blockquotes is test do
+               var md = """
+> Example:
+>
+>     sub status {
+>         print "working";
+>     }
+>
+> Or:
+>
+>     sub status {
+>         return "working";
+>     }
+"""
+
+               var html = """
+<blockquote>
+<p>Example:</p>
+<pre><code>sub status {
+    print &quot;working&quot;;
+}
+</code></pre>
+<p>Or:</p>
+<pre><code>sub status {
+    return &quot;working&quot;;
+}
+</code></pre>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_code_blocks is test do
+               var md = """
+       code block on the first line
+
+Regular text.
+
+    code block indented by spaces
+
+Regular text.
+
+       the lines in this block
+       all contain trailing spaces
+
+Regular Text.
+
+       code block on the last line
+"""
+
+               var html = """
+<pre><code>code block on the first line
+</code></pre>
+<p>Regular text.</p>
+<pre><code>code block indented by spaces
+</code></pre>
+<p>Regular text.</p>
+<pre><code>the lines in this block
+all contain trailing spaces
+</code></pre>
+<p>Regular Text.</p>
+<pre><code>code block on the last line
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_rules is test do
+               var md = """
+Dashes:
+
+---
+
+ ---
+
+  ---
+
+   ---
+
+       ---
+
+- - -
+
+ - - -
+
+  - - -
+
+   - - -
+
+       - - -
+
+
+Asterisks:
+
+***
+
+ ***
+
+  ***
+
+   ***
+
+       ***
+
+* * *
+
+ * * *
+
+  * * *
+
+   * * *
+
+       * * *
+
+
+Underscores:
+
+___
+
+ ___
+
+  ___
+
+   ___
+
+    ___
+
+_ _ _
+
+ _ _ _
+
+  _ _ _
+
+   _ _ _
+
+    _ _ _
+"""
+
+               var html = """
+<p>Dashes:</p>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>---
+</code></pre>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>- - -
+</code></pre>
+<p>Asterisks:</p>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>***
+</code></pre>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>* * *
+</code></pre>
+<p>Underscores:</p>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>___
+</code></pre>
+<hr />
+<hr />
+<hr />
+<hr />
+<pre><code>_ _ _
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_code_spans is test do
+               var md = """
+`<test a="` content of attribute `">`
+
+Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span>
+
+Here's how you put `` `backticks` `` in a code span.
+"""
+
+               var html = """
+<p><code>&lt;test a=&quot;</code> content of attribute <code>&quot;&gt;</code></p>
+<p>Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span></p>
+<p>Here's how you put <code>`backticks`</code> in a code span.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_images is test do
+               var md = """
+![Alt text](/path/to/img.jpg)
+
+![Alt text](/path/to/img.jpg "Optional title")
+
+Inline within a paragraph: [alt text](/url/).
+
+![alt text](/url/  "title preceded by two spaces")
+
+![alt text](/url/  "title has spaces afterward"  )
+
+![alt text](</url/>)
+
+![alt text](</url/> "with a title").
+
+![Empty]()
+
+![this is a stupid URL](http://example.com/(parens).jpg)
+
+
+![alt text][foo]
+
+  [foo]: /url/
+
+![alt text][bar]
+
+  [bar]: /url/ "Title here"
+"""
+
+               var html = """
+<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
+<p><img src="/path/to/img.jpg" alt="Alt text" title="Optional title" /></p>
+<p>Inline within a paragraph: <a href="/url/">alt text</a>.</p>
+<p><img src="/url/" alt="alt text" title="title preceded by two spaces" /></p>
+<p><img src="/url/" alt="alt text" title="title has spaces afterward" /></p>
+<p><img src="/url/" alt="alt text" /></p>
+<p><img src="/url/" alt="alt text" title="with a title" />.</p>
+<p><img src="" alt="Empty" /></p>
+<p><img src="http://example.com/(parens).jpg" alt="this is a stupid URL" /></p>
+<p><img src="/url/" alt="alt text" /></p>
+<p><img src="/url/" alt="alt text" title="Title here" /></p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_links1 is test do
+               var md = """
+Just a [URL](/url/).
+
+[URL and title](/url/ "title").
+
+[URL and title](/url/  "title preceded by two spaces").
+
+[URL and title](/url/  "title preceded by a tab").
+
+[URL and title](/url/ "title has spaces afterward"  ).
+
+[URL wrapped in angle brackets](</url/>).
+
+[URL w/ angle brackets + title](</url/> "Here's the title").
+
+[Empty]().
+
+[With parens in the URL](http://en.wikipedia.org/wiki/WIMP_(computing))
+
+(With outer parens and [parens in url](/foo(bar)))
+
+
+[With parens in the URL](/foo(bar) "and a title")
+
+(With outer parens and [parens in url](/foo(bar) "and a title"))
+"""
+
+               var html = """
+<p>Just a <a href="/url/">URL</a>.</p>
+<p><a href="/url/" title="title">URL and title</a>.</p>
+<p><a href="/url/" title="title preceded by two spaces">URL and title</a>.</p>
+<p><a href="/url/" title="title preceded by a tab">URL and title</a>.</p>
+<p><a href="/url/" title="title has spaces afterward">URL and title</a>.</p>
+<p><a href="/url/">URL wrapped in angle brackets</a>.</p>
+<p><a href="/url/" title="Here's the title">URL w/ angle brackets + title</a>.</p>
+<p><a href="">Empty</a>.</p>
+<p><a href="http://en.wikipedia.org/wiki/WIMP_(computing)">With parens in the URL</a></p>
+<p>(With outer parens and <a href="/foo(bar)">parens in url</a>)</p>
+<p><a href="/foo(bar)" title="and a title">With parens in the URL</a></p>
+<p>(With outer parens and <a href="/foo(bar)" title="and a title">parens in url</a>)</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_links2 is test do
+               var md = """
+Foo [bar][1].
+
+ Foo [bar][1].
+
+  Foo [bar][1].
+
+[1]: /url/  "Title"
+
+
+With [embedded [brackets]][b].
+
+
+ Indented [once][].
+
+  Indented [twice][].
+
+   Indented [thrice][].
+
+    Indented [four][] times.
+
+ [once]: /url
+
+  [twice]: /url
+
+   [thrice]: /url
+
+    [four]: /url
+
+
+[b]: /url/
+
+* * *
+
+[this][this] should work
+
+So should [this][this].
+
+And [this][].
+
+And [this][].
+
+And [this].
+
+But not [that][].
+
+Nor [that][].
+
+Nor [that].
+
+[Something in brackets like [this][] should work]
+
+[Same with [this].]
+
+In this case, [this](/somethingelse/) points to something else.
+
+Backslashing should suppress \\\[this] and [this\\\].
+
+[this]: foo
+
+
+* * *
+
+Here's one where the [link
+breaks] across lines.
+
+Here's another where the [link
+breaks] across lines, but with a line-ending space.
+
+
+[link breaks]: /url/
+"""
+
+               var html = """
+<p>Foo <a href="/url/" title="Title">bar</a>.</p>
+<p>Foo <a href="/url/" title="Title">bar</a>.</p>
+<p>Foo <a href="/url/" title="Title">bar</a>.</p>
+<p>With <a href="/url/">embedded [brackets]</a>.</p>
+<p>Indented <a href="/url">once</a>.</p>
+<p>Indented <a href="/url">twice</a>.</p>
+<p>Indented <a href="/url">thrice</a>.</p>
+<pre><code>Indented [four][] times.
+</code></pre>
+<pre><code>[four]: /url
+</code></pre>
+<hr />
+<p><a href="foo">this</a> should work</p>
+<p>So should <a href="foo">this</a>.</p>
+<p>And <a href="foo">this</a>.</p>
+<p>And <a href="foo">this</a>.</p>
+<p>And <a href="foo">this</a>.</p>
+<p>But not [that][].</p>
+<p>Nor [that][].</p>
+<p>Nor [that].</p>
+<p>[Something in brackets like <a href="foo">this</a> should work]</p>
+<p>[Same with <a href="foo">this</a>.]</p>
+<p>In this case, <a href="/somethingelse/">this</a> points to something else.</p>
+<p>Backslashing should suppress [this] and [this].</p>
+<hr />
+<p>Here's one where the <a href="/url/">link
+breaks</a> across lines.</p>
+<p>Here's another where the <a href="/url/">link
+breaks</a> across lines, but with a line-ending space.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_links3 is test do
+               var md = """
+This is the [simple case].
+
+[simple case]: /simple
+
+
+
+This one has a [line
+break].
+
+This one has a [line
+break] with a line-ending space.
+
+[line break]: /foo
+
+
+[this][that] and the [other]
+
+[this]: /this
+[that]: /that
+[other]: /other
+"""
+
+               var html = """
+<p>This is the <a href="/simple">simple case</a>.</p>
+<p>This one has a <a href="/foo">line
+break</a>.</p>
+<p>This one has a <a href="/foo">line
+break</a> with a line-ending space.</p>
+<p><a href="/that">this</a> and the <a href="/other">other</a></p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_nested is test do
+               var md = """
+> foo
+>
+> > bar
+>
+> foo
+"""
+
+               var html = """
+<blockquote>
+<p>foo</p>
+<blockquote>
+<p>bar</p>
+</blockquote>
+<p>foo</p>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_tidyness is test do
+               var md = """
+> A list within a blockquote:
+>
+> *    asterisk 1
+> *    asterisk 2
+> *    asterisk 3
+"""
+
+       var html = """
+<blockquote>
+<p>A list within a blockquote:</p>
+<ul>
+<li>asterisk 1</li>
+<li>asterisk 2</li>
+<li>asterisk 3</li>
+</ul>
+</blockquote>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_list is test do
+               var md = """
+## Unordered
+
+Asterisks tight:
+
+*      asterisk 1
+*      asterisk 2
+*      asterisk 3
+
+
+Asterisks loose:
+
+*      asterisk 1
+
+*      asterisk 2
+
+*      asterisk 3
+
+* * *
+
+Pluses tight:
+
++      Plus 1
++      Plus 2
++      Plus 3
+
+
+Pluses loose:
+
++      Plus 1
+
++      Plus 2
+
++      Plus 3
+
+* * *
+
+
+Minuses tight:
+
+-      Minus 1
+-      Minus 2
+-      Minus 3
+
+
+Minuses loose:
+
+-      Minus 1
+
+-      Minus 2
+
+-      Minus 3
+
+
+## Ordered
+
+Tight:
+
+1.     First
+2.     Second
+3.     Third
+
+and:
+
+1. One
+2. Two
+3. Three
+
+
+Loose using tabs:
+
+1.     First
+
+2.     Second
+
+3.     Third
+
+and using spaces:
+
+1. One
+
+2. Two
+
+3. Three
+
+Multiple paragraphs:
+
+1.     Item 1, graf one.
+
+       Item 2. graf two. The quick brown fox jumped over the lazy dog's
+       back.
+
+2.     Item 2.
+
+3.     Item 3.
+
+
+
+## Nested
+
+*      Tab
+       *       Tab
+               *       Tab
+
+Here's another:
+
+1. First
+2. Second:
+       * Fee
+       * Fie
+       * Foe
+3. Third
+
+Same thing but with paragraphs:
+
+1. First
+
+2. Second:
+       * Fee
+       * Fie
+       * Foe
+
+3. Third
+"""
+
+               var html = """
+<h2>Unordered</h2>
+<p>Asterisks tight:</p>
+<ul>
+<li>asterisk 1</li>
+<li>asterisk 2</li>
+<li>asterisk 3</li>
+</ul>
+<p>Asterisks loose:</p>
+<ul>
+<li>
+<p>asterisk 1</p>
+</li>
+<li>
+<p>asterisk 2</p>
+</li>
+<li>
+<p>asterisk 3</p>
+</li>
+</ul>
+<hr />
+<p>Pluses tight:</p>
+<ul>
+<li>Plus 1</li>
+<li>Plus 2</li>
+<li>Plus 3</li>
+</ul>
+<p>Pluses loose:</p>
+<ul>
+<li>
+<p>Plus 1</p>
+</li>
+<li>
+<p>Plus 2</p>
+</li>
+<li>
+<p>Plus 3</p>
+</li>
+</ul>
+<hr />
+<p>Minuses tight:</p>
+<ul>
+<li>Minus 1</li>
+<li>Minus 2</li>
+<li>Minus 3</li>
+</ul>
+<p>Minuses loose:</p>
+<ul>
+<li>
+<p>Minus 1</p>
+</li>
+<li>
+<p>Minus 2</p>
+</li>
+<li>
+<p>Minus 3</p>
+</li>
+</ul>
+<h2>Ordered</h2>
+<p>Tight:</p>
+<ol>
+<li>First</li>
+<li>Second</li>
+<li>Third</li>
+</ol>
+<p>and:</p>
+<ol>
+<li>One</li>
+<li>Two</li>
+<li>Three</li>
+</ol>
+<p>Loose using tabs:</p>
+<ol>
+<li>
+<p>First</p>
+</li>
+<li>
+<p>Second</p>
+</li>
+<li>
+<p>Third</p>
+</li>
+</ol>
+<p>and using spaces:</p>
+<ol>
+<li>
+<p>One</p>
+</li>
+<li>
+<p>Two</p>
+</li>
+<li>
+<p>Three</p>
+</li>
+</ol>
+<p>Multiple paragraphs:</p>
+<ol>
+<li>
+<p>Item 1, graf one.</p>
+<p>Item 2. graf two. The quick brown fox jumped over the lazy dog's
+back.</p>
+</li>
+<li>
+<p>Item 2.</p>
+</li>
+<li>
+<p>Item 3.</p>
+</li>
+</ol>
+<h2>Nested</h2>
+<ul>
+<li>Tab
+<ul>
+<li>Tab
+<ul>
+<li>Tab</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+<p>Here's another:</p>
+<ol>
+<li>First</li>
+<li>Second:
+<ul>
+<li>Fee</li>
+<li>Fie</li>
+<li>Foe</li>
+</ul>
+</li>
+<li>Third</li>
+</ol>
+<p>Same thing but with paragraphs:</p>
+<ol>
+<li>
+<p>First</p>
+</li>
+<li>
+<p>Second:</p>
+<ul>
+<li>Fee</li>
+<li>Fie</li>
+<li>Foe</li>
+</ul>
+</li>
+<li>
+<p>Third</p>
+</li>
+</ol>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_strong_em is test do
+               var md = """
+***This is strong and em.***
+
+So is ***this*** word.
+
+___This is strong and em.___
+
+So is ___this___ word.
+"""
+
+               var html = """
+<p><em><strong>This is strong and em.</strong></em></p>
+<p>So is <em><strong>this</strong></em> word.</p>
+<p><em><strong>This is strong and em.</strong></em></p>
+<p>So is <em><strong>this</strong></em> word.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_tabs is test do
+               var md = """
++      this is a list item
+       indented with tabs
+
++   this is a list item
+    indented with spaces
+
+Code:
+
+       this code block is indented by one tab
+
+And:
+
+               this code block is indented by two tabs
+
+And:
+
+       +       this is an example list item
+               indented with tabs
+
+       +   this is an example list item
+           indented with spaces
+"""
+
+               var html = """
+<ul>
+<li>
+<p>this is a list item
+indented with tabs</p>
+</li>
+<li>
+<p>this is a list item
+indented with spaces</p>
+</li>
+</ul>
+<p>Code:</p>
+<pre><code>this code block is indented by one tab
+</code></pre>
+<p>And:</p>
+<pre><code>    this code block is indented by two tabs
+</code></pre>
+<p>And:</p>
+<pre><code>+   this is an example list item
+       indented with tabs
+
++   this is an example list item
+    indented with spaces
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_inline_html1 is test do
+               var md = """
+Here's a simple block:
+
+<div>
+       foo
+</div>
+
+This should be a code block, though:
+
+       <div>
+               foo
+       </div>
+
+As should this:
+
+       <div>foo</div>
+
+Now, nested:
+
+<div>
+       <div>
+               <div>
+                       foo
+               </div>
+       </div>
+</div>
+
+This should just be an HTML comment:
+
+<!-- Comment -->
+
+Multiline:
+
+<!--
+Blah
+Blah
+-->
+
+Code block:
+
+       <!-- Comment -->
+
+Just plain comment, with trailing spaces on the line:
+
+<!-- foo -->
+
+Code:
+
+       <hr />
+
+Hr's:
+
+<hr>
+
+<hr />
+
+<hr />
+
+<hr>
+
+<hr />
+
+<hr />
+
+<hr class="foo" />
+
+<hr class="foo"/>
+
+<hr class="foo" >
+"""
+
+               var html = """
+<p>Here's a simple block:</p>
+<div>
+       foo
+</div>
+<p>This should be a code block, though:</p>
+<pre><code>&lt;div&gt;
+       foo
+&lt;/div&gt;
+</code></pre>
+<p>As should this:</p>
+<pre><code>&lt;div&gt;foo&lt;/div&gt;
+</code></pre>
+<p>Now, nested:</p>
+<div>
+       <div>
+               <div>
+                       foo
+               </div>
+       </div>
+</div>
+<p>This should just be an HTML comment:</p>
+<!-- Comment -->
+<p>Multiline:</p>
+<!--
+Blah
+Blah
+-->
+<p>Code block:</p>
+<pre><code>&lt;!-- Comment --&gt;
+</code></pre>
+<p>Just plain comment, with trailing spaces on the line:</p>
+<!-- foo -->
+<p>Code:</p>
+<pre><code>&lt;hr /&gt;
+</code></pre>
+<p>Hr's:</p>
+<hr>
+<hr />
+<hr />
+<hr>
+<hr />
+<hr />
+<hr class="foo" />
+<hr class="foo"/>
+<hr class="foo" >
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_inline_html2 is test do
+               var md = """
+Simple block on one line:
+
+<div>foo</div>
+
+And nested without indentation:
+
+<div>
+<div>
+<div>
+foo
+</div>
+<div style=">"/>
+</div>
+<div>bar</div>
+</div>
+
+And with attributes:
+
+<div>
+       <div>
+       </div>
+</div>
+
+This was broken in 1.0.2b7:
+
+<div class="inlinepage">
+<div class="toggleableend">
+foo
+</div>
+</div>
+"""
+
+               var html = """
+<p>Simple block on one line:</p>
+<div>foo</div>
+<p>And nested without indentation:</p>
+<div>
+<div>
+<div>
+foo
+</div>
+<div style=">"/>
+</div>
+<div>bar</div>
+</div>
+<p>And with attributes:</p>
+<div>
+       <div>
+       </div>
+</div>
+<p>This was broken in 1.0.2b7:</p>
+<div class="inlinepage">
+<div class="toggleableend">
+foo
+</div>
+</div>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_daring_inline_html3 is test do
+               var md = """
+Paragraph one.
+
+<!-- This is a simple comment -->
+
+<!--
+       This is another comment.
+-->
+
+Paragraph two.
+
+<!-- one comment block -- -- with two comments -->
+
+The end.
+"""
+
+               var html = """
+<p>Paragraph one.</p>
+<!-- This is a simple comment -->
+<!--
+       This is another comment.
+-->
+<p>Paragraph two.</p>
+<!-- one comment block -- -- with two comments -->
+<p>The end.</p>
+"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_github.nit b/lib/markdown2/tests/test_markdown_github.nit
new file mode 100644 (file)
index 0000000..04c04a4
--- /dev/null
@@ -0,0 +1,313 @@
+# 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 Github mode
+module test_markdown_github is test
+
+import test_markdown
+import test_markdown_location
+import test_markdown_md
+import test_markdown_man
+import test_markdown_latex
+
+redef class TestMarkdown
+       redef var md_parser is lazy do
+               var parser = super
+               parser.github_mode = true
+               return parser
+       end
+end
+
+class TestGithubLocation
+       super TestMarkdownLocation
+       test
+
+       fun test_github_strike is test do
+               var md = """
+A ~striked~ text.
+"""
+               var loc = """
+MdDocument: 1,1--1,17
+  MdParagraph: 1,1--1,17
+    MdText: 1,1--1,2
+    MdStrike: 1,3--1,11
+      MdText: 1,4--1,10
+    MdText: 1,12--1,17
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_github_strike2 is test do
+               var md = """
+A ~~striked~~ text.
+"""
+               var loc = """
+MdDocument: 1,1--1,19
+  MdParagraph: 1,1--1,19
+    MdText: 1,1--1,2
+    MdStrike: 1,3--1,13
+      MdText: 1,5--1,11
+    MdText: 1,14--1,19
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_github_super is test do
+               var md = """
+A ^supered^ text.
+"""
+               var loc = """
+MdDocument: 1,1--1,17
+  MdParagraph: 1,1--1,17
+    MdText: 1,1--1,2
+    MdSuper: 1,3--1,11
+      MdText: 1,4--1,10
+    MdText: 1,12--1,17
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_github_super2 is test do
+               var md = """
+A ^^supered^^ text.
+"""
+               var loc = """
+MdDocument: 1,1--1,19
+  MdParagraph: 1,1--1,19
+    MdText: 1,1--1,2
+    MdSuper: 1,3--1,13
+      MdText: 1,5--1,11
+    MdText: 1,14--1,19
+"""
+               assert md_to_loc(md) == loc
+       end
+end
+
+class TestGithubHtml
+       super TestMarkdownHtml
+       test
+
+       fun test_strike1 is test do
+               var md = """foo ~bar~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike2 is test do
+               var md = """foo ~~bar~~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike3 is test do
+               var md = """foo ~~~bar~~~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike4 is test do
+               var md = """foo ~~~~bar~~~~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike5 is test do
+               var md = """foo ~~~~~bar~~~~~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike6 is test do
+               var md = """foo ~~~~~~bar~~~~~~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad is test do
+               var md = """foo ~bar baz\n"""
+               var html = """<p>foo ~bar baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad2 is test do
+               var md = """foo ~~bar~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad3 is test do
+               var md = """foo ~~~bar~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad4 is test do
+               var md = """foo ~~~~bar~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad5 is test do
+               var md = """foo ~~~~~bar~ baz\n"""
+               var html = """<p>foo <del>bar</del> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad6 is test do
+               var md = """foo bar~ baz\n"""
+               var html = """<p>foo bar~ baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_strike_bad7 is test do
+               var md = """foo ~bar~~~~ baz\n"""
+               var html = """<p>foo <del>bar</del>~~~ baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super1 is test do
+               var md = """foo ^bar^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super2 is test do
+               var md = """foo ^^bar^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super3 is test do
+               var md = """foo ^^^bar^^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super4 is test do
+               var md = """foo ^^^^bar^^^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super5 is test do
+               var md = """foo ^^^^^bar^^^^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super6 is test do
+               var md = """foo ^^^^^^bar^^^^^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad1 is test do
+               var md = """foo ^bar baz\n"""
+               var html = """<p>foo ^bar baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad is test do
+               var md = """foo ^^bar^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad3 is test do
+               var md = """foo ^^^bar^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad4 is test do
+               var md = """foo ^^^^bar^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad5 is test do
+               var md = """foo ^^^^^bar^ baz\n"""
+               var html = """<p>foo <sup>bar</sup> baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad6 is test do
+               var md = """foo bar^ baz\n"""
+               var html = """<p>foo bar^ baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_super_bad7 is test do
+               var md = """foo ^bar^^^^ baz\n"""
+               var html = """<p>foo <sup>bar</sup>^^^ baz</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
+
+class TestGithubMd
+       super TestMarkdownMd
+       test
+
+       fun test_strike_md is test do
+               var md = """~~foo~~\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test_super_md is test do
+               var md = """^^foo^^\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestGithubMan
+       super TestMarkdownMan
+       test
+
+       fun test_strike_man is test do
+               var md = """~~foo~~\n"""
+               var man = """\n[STRIKEOUT:foo]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_super_man is test do
+               var md = """^foo^\n"""
+               var man = """\nfoo\n"""
+               assert md_to_man(md) == man
+       end
+end
+
+class TestGithubLatex
+       super TestMarkdownLatex
+       test
+
+       fun test_strike_latex is test do
+               var md = """
+A ~~super~~ text.
+"""
+               var tex = """
+A \\sout{super} text.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_super_latex is test do
+               var md = """
+A ^super^ text.
+"""
+               var tex = """
+A \\textsuperscript{super} text.
+"""
+               assert md_to_tex(md) == tex
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_headings_id.nit b/lib/markdown2/tests/test_markdown_headings_id.nit
new file mode 100644 (file)
index 0000000..28f91a3
--- /dev/null
@@ -0,0 +1,61 @@
+# 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.
+
+# Test for markdown headings id generation
+module test_markdown_headings_id is test
+
+import test_markdown
+
+class TestMarkdownHeadingsId
+       super TestMarkdownHtml
+       test
+
+       redef var html_renderer = new HtmlRenderer(true)
+
+       fun test_multiple_ids is test do
+               var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n"""
+               var html = """<h1 id="foo">foo</h1>\n<h2 id="foo_1">foo</h2>\n<h3 id="foo_2">foo</h3>\n<h4 id="foo_3">foo</h4>\n<h5 id="foo_4">foo</h5>\n<h6 id="foo_5">foo</h6>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_escape_ids is test do
+               var md = """# foo *bar* \\*baz\\*\n"""
+               var html = """<h1 id="foo_bar_baz">foo <em>bar</em> *baz*</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_escape_ids2 is test do
+               var md = """# foo#\n"""
+               var html = """<h1 id="foo">foo#</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_avoid_spaces is test do
+               var md = """#                  foo                     \n"""
+               var html = """<h1 id="foo">foo</h1>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_remove_atx_trailing is test do
+               var md = """## foo ##\n  ###   bar    ###\n"""
+               var html = """<h2 id="foo">foo</h2>\n<h3 id="bar">bar</h3>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_avoid_escaped_chars is test do
+               var md = """### foo \\###\n## foo #\\##\n# foo \\#\n"""
+               var html = """<h3 id="foo_">foo ###</h3>\n<h2 id="foo__1">foo ###</h2>\n<h1 id="foo__2">foo #</h1>\n"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_inlines.nit b/lib/markdown2/tests/test_markdown_inlines.nit
new file mode 100644 (file)
index 0000000..6a21084
--- /dev/null
@@ -0,0 +1,335 @@
+# 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 htmlress or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tests for markdown inline constructs
+module test_markdown_inlines is test
+
+import test_markdown
+
+class TestMarkdownInlines
+       super TestMarkdownHtml
+       test
+
+       fun test_inlines_emph1 is test do
+               var md = """
+*single asterisks*
+
+_single underscores_
+
+**double asterisks**
+
+__double underscores__
+"""
+               var html = """<p><em>single asterisks</em></p>
+<p><em>single underscores</em></p>
+<p><strong>double asterisks</strong></p>
+<p><strong>double underscores</strong></p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_emph2 is test do
+               var md = "un*frigging*believable"
+               var html = "<p>un<em>frigging</em>believable</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_emph3 is test do
+               var md = "Con _cat_ this"
+               var html = "<p>Con <em>cat</em> this</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_emph_ext is test do
+               var md = "Con_cat_this"
+               var html = "<p>Con_cat_this</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_xml1 is test do
+               var md = """
+This is a regular paragraph.
+
+<table>
+    <tr>
+        <td>Foo</td>
+    </tr>
+</table>
+
+This is another regular paragraph.
+"""
+               var html = """
+<p>This is a regular paragraph.</p>
+<table>
+    <tr>
+        <td>Foo</td>
+    </tr>
+</table>
+<p>This is another regular paragraph.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_xml2 is test do
+               var md = """
+This is an image <img src="foo/bar" alt="baz"/> in a regular paragraph.
+"""
+               var html = """
+<p>This is an image <img src="foo/bar" alt="baz"/> in a regular paragraph.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_xml3 is test do
+               var md = """
+<div style=">"/>
+"""
+               var html = """
+<div style=">"/>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_xml4 is test do
+               var md = """
+<p>This is an example of a block element that should be escaped.</p>
+<p>Idem for the second paragraph.</p>
+"""
+               assert md_to_html(md) == md
+       end
+
+       fun test_inlines_xml5 is test do
+               var md = """
+# Some more XML tests
+
+<p>This is an example of a block element that should be escaped.</p>
+<p>Idem for the second paragraph.</p>
+
+With a *md paragraph*!
+"""
+               var html = """
+<h1>Some more XML tests</h1>
+<p>This is an example of a block element that should be escaped.</p>
+<p>Idem for the second paragraph.</p>
+<p>With a <em>md paragraph</em>!</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_escape_bad_html is test do
+               var md = "-1 if < , +1 if > and 0 otherwise"
+               var html = "<p>-1 if &lt; , +1 if &gt; and 0 otherwise</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_span_code1 is test do
+               var md = "Use the `printf()` function."
+               var html = "<p>Use the <code>printf()</code> function.</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_span_code2 is test do
+               var md = "``There is a literal backtick (`) here.``"
+               var html = "<p><code>There is a literal backtick (`) here.</code></p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_span_code3 is test do
+               var md = """
+A single backtick in a code span: `` ` ``
+
+A backtick-delimited string in a code span: `` `foo` ``
+"""
+               var html = """
+<p>A single backtick in a code span: <code>`</code></p>
+<p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_span_code4 is test do
+               var md = "Please don't use any `<blink>` tags."
+               var html = "<p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_span_code5 is test do
+               var md = "`&#8212;` is the decimal-encoded equivalent of `&mdash;`."
+               var html = "<p><code>&amp;#8212;</code> is the decimal-encoded equivalent of <code>&amp;mdash;</code>.</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_escape1 is test do
+               var md = "\\*this text is surrounded by literal asterisks\\*"
+               var html = "<p>*this text is surrounded by literal asterisks*</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_escape2 is test do
+               var md = "1986\\. What a great season."
+               var html = "<p>1986. What a great season.</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_escape3 is test do
+               var md = "Ben & Lux"
+               var html = "<p>Ben &amp; Lux</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link1 is test do
+               var md = """
+This is [an example](http://example.com/ "Title") inline link.
+
+[This link](http://example.net/) has no title attribute.
+"""
+               var html = """<p>This is <a href="http://example.com/" title="Title">an example</a> inline link.</p>
+<p><a href="http://example.net/">This link</a> has no title attribute.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link2 is test do
+               var md = "See my [About](/about/) page for details."
+               var html = "<p>See my <a href=\"/about/\">About</a> page for details.</p>\n"
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link3 is test do
+               var md = """
+This is [an example][id] reference-style link.
+
+Some lorem ipsum
+
+[id]: http://example.com/  "Optional Title Here"
+
+Some other lipsum
+"""
+               var html = """
+<p>This is <a href="http://example.com/" title="Optional Title Here">an example</a> reference-style link.</p>
+<p>Some lorem ipsum</p>
+<p>Some other lipsum</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link4 is test do
+               var md = """
+This is multiple examples: [foo][1], [bar][2], [baz][3].
+
+[1]: http://example.com/  "Optional Title Here"
+[2]: http://example.com/  'Optional Title Here'
+[3]: http://example.com/  (Optional Title Here)
+"""
+               var html = """
+<p>This is multiple examples: <a href="http://example.com/" title="Optional Title Here">foo</a>, <a href="http://example.com/" title="Optional Title Here">bar</a>, <a href="http://example.com/" title="Optional Title Here">baz</a>.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link5 is test do
+               var md = """
+This is multiple examples: [foo][a], [bar][A], [a].
+
+[a]: http://example.com/  "Optional Title Here"
+"""
+               var html = """<p>This is multiple examples: <a href="http://example.com/" title="Optional Title Here">foo</a>, <a href="http://example.com/" title="Optional Title Here">bar</a>, <a href="http://example.com/" title="Optional Title Here">a</a>.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link6 is test do
+               var md = """
+I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][].
+
+[Google]: http://google.com/        "Google"
+[Yahoo]: http://search.yahoo.com/   "Yahoo Search"
+[MSN]: http://search.msn.com/       "MSN Search"
+"""
+               var html = """<p>I get 10 times more traffic from <a href="http://google.com/" title="Google">Google</a> than from <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link7 is test do
+               var md = """
+Visit [Daring Fireball][] for more information.
+
+[Daring Fireball]: http://daringfireball.net/
+"""
+               var html = """<p>Visit <a href="http://daringfireball.net/">Daring Fireball</a> for more information.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link8 is test do
+               var md = """
+This one has a [line
+break].
+
+This one has a [line
+break] with a line-ending space.
+
+[line break]: /foo
+"""
+               var html = """
+<p>This one has a <a href="/foo">line
+break</a>.</p>
+<p>This one has a <a href="/foo">line
+break</a> with a line-ending space.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_link9 is test do
+               var md = """
+Foo [bar][].
+
+Foo [bar](/url/ "Title with \\"quotes\\" inside").
+
+
+[bar]: /url/ "Title with \\"quotes\\" inside"
+"""
+               var html = """
+<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>
+<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_img1 is test do
+               var md = """
+![Alt text](/path/to/img.jpg)
+
+![Alt text](/path/to/img.jpg "Optional title")
+"""
+               var html = """
+<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
+<p><img src="/path/to/img.jpg" alt="Alt text" title="Optional title" /></p>
+"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_inlines_img2 is test do
+               var md = """
+![Alt text][id]
+
+[id]: url/to/image  "Optional title attribute"
+"""
+               var html = """
+<p><img src="url/to/image" alt="Alt text" title="Optional title attribute" /></p>
+"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_issues.nit b/lib/markdown2/tests/test_markdown_issues.nit
new file mode 100644 (file)
index 0000000..c8deb1b
--- /dev/null
@@ -0,0 +1,152 @@
+# 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 related to markdown issues from the Git repo
+#
+# Fixing:
+#
+# * 1525: lib/markdown: issue with nested fences
+# * 2507: markdown: some lines are lost in verbatim blocks inside a list
+#
+# See <https://github.com/nitlang/nit/issues>.
+module test_markdown_issues is test
+
+import test_markdown
+
+class TestMarkdownIssues
+       super TestMarkdownHtml
+       test
+
+       # See <https://github.com/nitlang/nit/issues/1525>.
+       fun test_issue_1525_1 is test do
+               var md = """
+A fence within a fence
+~~~~~~
+some code:
+~~~
+some other code
+~~~
+~~~~~~
+"""
+               var html = """
+<p>A fence within a fence</p>
+<pre><code>some code:
+~~~
+some other code
+~~~
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       # See <https://github.com/nitlang/nit/issues/1525>.
+       fun test_issue_1525_2 is test do
+               var md = """
+A fence within a fence
+~~~
+some code:
+```
+some other code
+```
+~~~
+"""
+               var html = """
+<p>A fence within a fence</p>
+<pre><code>some code:
+```
+some other code
+```
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       # See <https://github.com/nitlang/nit/issues/1525>.
+       fun test_issue_1525_3 is test do
+               var md = """
+A fence within a fence
+```
+some code:
+~~~
+some other code
+~~~
+```
+"""
+               var html = """
+<p>A fence within a fence</p>
+<pre><code>some code:
+~~~
+some other code
+~~~
+</code></pre>
+"""
+               assert md_to_html(md) == html
+       end
+
+       # See <https://github.com/nitlang/nit/issues/2507>.
+       fun test_issue_2507_1 is test do
+               var md = """
+*   4 spaces, `asdf` and `tab.` are lost
+    ~~~
+    a
+    as
+    asd
+    asdf
+               tab.
+    asdfg
+    ~~~
+"""
+               var html = """
+<ul>
+<li>4 spaces, <code>asdf</code> and <code>tab.</code> are lost
+<pre><code>a
+as
+asd
+asdf
+       tab.
+asdfg
+</code></pre>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+
+       # See <https://github.com/nitlang/nit/issues/2507>.
+       fun test_issue_2507_2 is test do
+               var md = """
+* 2 spaces, `as` is lost
+
+  ~~~
+  a
+  as
+  asd
+  asdf
+  ~~~
+"""
+               var html = """
+<ul>
+<li>
+<p>2 spaces, <code>as</code> is lost</p>
+<pre><code>a
+as
+asd
+asdf
+</code></pre>
+</li>
+</ul>
+"""
+               assert md_to_html(md) == html
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_latex.nit b/lib/markdown2/tests/test_markdown_latex.nit
new file mode 100644 (file)
index 0000000..ec85616
--- /dev/null
@@ -0,0 +1,542 @@
+# 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 LaTeX
+module test_markdown_latex is test
+
+import test_markdown
+import markdown_latex_rendering
+
+# Abstract test class that defines the test methods for LaTeX rendering
+abstract class TestMarkdownLatex
+       super TestMarkdown
+
+       # LaTeX renderer used in tests
+       var tex_renderer = new LatexRenderer
+
+       # Render the `md` string as LaTeX format
+       fun md_to_tex(md: String): String do
+               var node = parse_md(md)
+               return tex_renderer.render(node)
+       end
+end
+
+class TestLatexRendering
+       super TestMarkdownLatex
+       test
+
+       fun after_test is after do
+               tex_renderer.wrap_document = false
+               tex_renderer.use_listings = false
+       end
+
+       fun test_document_wrapper is test do
+               var md = """
+This example needs a document wrapper.
+"""
+
+               var tex = """
+\\documentclass[letter,10pt]{article}
+
+\\usepackage[utf8]{inputenc}
+\\usepackage{hyperref}
+\\usepackage{graphicx}
+\\usepackage{ulem}
+
+\\begin{document}
+
+This example needs a document wrapper.
+
+\\end{document}
+"""
+               tex_renderer.wrap_document = true
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_atx_headings is test do
+               var md = """
+# Title 1
+## Title 2
+### Title 3
+#### Title 4
+##### Title 5
+###### Title 6
+"""
+               var tex = """
+\\section{Title 1}
+
+\\subsection{Title 2}
+
+\\subsubsection{Title 3}
+
+\\paragraph{Title 4}
+
+\\subparagraph{Title 5}
+
+\\textbf{Title 6}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_blockquotes is test do
+               var md = """
+> this is a
+> multiline quote
+"""
+               var tex = """
+\\begin{quote}
+  this is a
+  multiline quote
+\\end{quote}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_thematic_breaks is test do
+               var md = """
+* * *
+"""
+               var tex = """
+\\begin{center}\\rule{3in}{0.4pt}\\end{center}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_paragraphs is test do
+               var md = """
+a paragraph
+on two lines
+
+another paragraph
+"""
+               var tex = """
+a paragraph
+on two lines
+
+another paragraph
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_html_block is test do
+               var md = """
+<p>
+       <a href="url">foo</a>
+</p>
+               """
+               var tex = """
+\\begin{verbatim}
+<p>
+       <a href="url">foo</a>
+</p>
+\\end{verbatim}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_indented_code is test do
+               var md = """
+    first line
+    second line
+"""
+               var tex = """
+\\begin{verbatim}
+first line
+second line
+\\end{verbatim}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_indented_code_with_listings is test do
+               var md = """
+    first line
+    second line
+"""
+               var tex = """
+\\begin{lstlisting}
+first line
+second line
+\\end{lstlisting}
+"""
+               tex_renderer.use_listings = true
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_fenced_code is test do
+               var md = """
+~~~
+first line
+second line
+~~~
+"""
+               var tex = """
+\\begin{verbatim}
+first line
+second line
+\\end{verbatim}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_fenced_code_with_listings is test do
+               var md = """
+~~~
+first line
+second line
+~~~
+"""
+               var tex = """
+\\begin{lstlisting}
+first line
+second line
+\\end{lstlisting}
+"""
+               tex_renderer.use_listings = true
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_fenced_code_with_listings_and_language is test do
+               var md = """
+~~~c
+first line
+second line
+~~~
+"""
+               var tex = """
+\\begin{lstlisting}[language=c]
+first line
+second line
+\\end{lstlisting}
+"""
+               tex_renderer.use_listings = true
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_list_ordered is test do
+               var md = """
+1) item 1
+2) item 2
+"""
+               var tex = """
+\\begin{enumerate}
+  \\item
+    item 1
+  \\item
+    item 2
+\\end{enumerate}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_list_ordered_with_starting_number is test do
+               var md = """
+4) item 1
+5) item 2
+"""
+               var tex = """
+\\begin{enumerate}
+  \\setcounter{enumi}{4}
+  \\item
+    item 1
+  \\item
+    item 2
+\\end{enumerate}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_list_unordered is test do
+               var md = """
+* item 1
+* item 2
+"""
+               var tex = """
+\\begin{itemize}
+  \\item
+    item 1
+  \\item
+    item 2
+\\end{itemize}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_list_nested is test do
+               var md = """
+* item 1
+* item 2
+   1) item 3
+   2) item 4
+"""
+               var tex = """
+\\begin{itemize}
+  \\item
+    item 1
+  \\item
+    item 2
+    \\begin{enumerate}
+      \\item
+        item 3
+      \\item
+        item 4
+    \\end{enumerate}
+\\end{itemize}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_ordered_list_nested is test do
+               var md = """
+4) item 1
+5) item 2
+
+   4) item 3
+   5) item 4
+
+      4) item 3
+      5) item 4
+
+         4) item 3
+         5) item 4
+"""
+               var tex = """
+\\begin{enumerate}
+  \\setcounter{enumi}{4}
+  \\item
+    item 1
+  \\item
+    item 2
+    \\begin{enumerate}
+      \\setcounter{enumii}{4}
+      \\item
+        item 3
+      \\item
+        item 4
+        \\begin{enumerate}
+          \\setcounter{enumiii}{4}
+          \\item
+            item 3
+          \\item
+            item 4
+            \\begin{enumerate}
+              \\setcounter{enumiv}{4}
+              \\item
+                item 3
+              \\item
+                item 4
+            \\end{enumerate}
+        \\end{enumerate}
+    \\end{enumerate}
+\\end{enumerate}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_list_and_blockquote is test do
+               var md = """
+* item 1
+* item 2
+   > quote 1
+   > quote 2
+"""
+               var tex = """
+\\begin{itemize}
+  \\item
+    item 1
+  \\item
+    item 2
+    \\begin{quote}
+      quote 1
+      quote 2
+    \\end{quote}
+\\end{itemize}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_blockquote_and_list is test do
+               var md = """
+> line 1
+> line 2
+> * item 1
+> * item 2
+"""
+               var tex = """
+\\begin{quote}
+  line 1
+  line 2
+  \\begin{itemize}
+    \\item
+      item 1
+    \\item
+      item 2
+  \\end{itemize}
+\\end{quote}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_code is test do
+               var md = """
+An `inline code`.
+"""
+               var tex = """
+An \\texttt{inline code}.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_emphasis is test do
+               var md = """
+An *emphasis* and a **strong emphasis**.
+"""
+               var tex = """
+An \\textit{emphasis} and a \\textbf{strong emphasis}.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_autolink is test do
+               var md = """
+<http://test>
+"""
+               var tex = """
+\\url{http://test}
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_link is test do
+               var md = """
+A [link](url/).
+"""
+               var tex = """
+A \\href{url/}{link}.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_link_with_title is test do
+               var md = """
+A [link](url/ "with a title").
+"""
+               var tex = """
+A \\href{url/}{link (with a title)}.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_image is test do
+               var md = """
+![image](url/).
+"""
+               var tex = """
+\\includegraphics{url/}.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_softbreak is test do
+               var md = """
+A soft
+break.
+"""
+               var tex = """
+A soft
+break.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_hardbreak is test do
+               var md = """
+A hard\\
+break.
+"""
+               var tex = """
+A hard
+break.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_escaped is test do
+               var md = """
+An escaped \\*.
+"""
+               var tex = """
+An escaped *.
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_forbidden_chars is test do
+               var md = """
+%${_><#&}\\
+"""
+               var tex = """
+\\%\\$\\{\\_\\textgreater\\textless\\#\\&\\}\\textbackslash
+"""
+               assert md_to_tex(md) == tex
+       end
+
+       fun test_full_document is test do
+               var md = """
+# Title
+
+A paragraph.
+
+## Another title
+
+A list:
+
+1. item 1
+2. item 2
+
+A code example:
+
+    line 1
+       line 2
+
+Another paragraph.
+"""
+               var tex = """
+\\section{Title}
+
+A paragraph.
+
+\\subsection{Another title}
+
+A list:
+
+\\begin{enumerate}
+  \\item
+    item 1
+  \\item
+    item 2
+\\end{enumerate}
+
+A code example:
+
+\\begin{verbatim}
+line 1
+line 2
+\\end{verbatim}
+
+Another paragraph.
+"""
+               assert md_to_tex(md) == tex
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_location.nit b/lib/markdown2/tests/test_markdown_location.nit
new file mode 100644 (file)
index 0000000..4aaa218
--- /dev/null
@@ -0,0 +1,913 @@
+# 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 nodes location
+module test_markdown_location is test
+
+import test_markdown
+
+abstract class TestMarkdownLocation
+       super TestMarkdown
+
+       redef var md_parser do
+               var parser = super
+               parser.github_mode = true
+               parser.wikilinks_mode = true
+               return parser
+       end
+
+       fun md_to_loc(md: String): String do
+               var node = parse_md(md)
+               var v = new TestMarkdownLocationVisitor
+               v.enter_visit(node)
+               return v.buffer.to_s
+       end
+end
+
+class TestMarkdownLocationVisitor
+       super MdVisitor
+
+       var buffer = new Buffer
+       var indent = 0
+
+       fun print_loc(node: MdNode) do
+               buffer.append "{"  " * indent}{node.class_name}: {node.location}\n"
+               indent += 1
+               node.visit_all(self)
+               indent -= 1
+       end
+
+       redef fun visit(node) do print_loc(node)
+end
+
+class TestLocationOutput
+       super TestMarkdownLocation
+       test
+
+       fun test_atx_headings1 is test do
+               var md = """
+# title 1
+## title 2
+### title 3
+#### title 4
+##### title 5
+###### title 6
+"""
+               var loc = """
+MdDocument: 1,1--6,14
+  MdHeading: 1,1--1,9
+    MdText: 1,3--1,9
+  MdHeading: 2,1--2,10
+    MdText: 2,4--2,10
+  MdHeading: 3,1--3,11
+    MdText: 3,5--3,11
+  MdHeading: 4,1--4,12
+    MdText: 4,6--4,12
+  MdHeading: 5,1--5,13
+    MdText: 5,7--5,13
+  MdHeading: 6,1--6,14
+    MdText: 6,8--6,14
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_atx_headings_with_trailings is test do
+               var md = """
+# title 1 #
+## title 2 ##
+### title 3 ###
+#### title 4 ####
+##### title 5 #####
+###### title 6 ######
+"""
+               var loc = """
+MdDocument: 1,1--6,21
+  MdHeading: 1,1--1,11
+    MdText: 1,3--1,9
+  MdHeading: 2,1--2,13
+    MdText: 2,4--2,10
+  MdHeading: 3,1--3,15
+    MdText: 3,5--3,11
+  MdHeading: 4,1--4,17
+    MdText: 4,6--4,12
+  MdHeading: 5,1--5,19
+    MdText: 5,7--5,13
+  MdHeading: 6,1--6,21
+    MdText: 6,8--6,14
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_settext_headings is test do
+               var md = """
+title 1
+=======
+
+title 2
+-------
+"""
+               var loc = """
+MdDocument: 1,1--5,7
+  MdHeading: 1,1--2,7
+    MdText: 1,1--1,7
+  MdHeading: 4,1--5,7
+    MdText: 4,1--4,7
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_indented_code_spaces is test do
+               var md = """
+    some code
+
+       multi lines
+"""
+               var loc = """
+MdDocument: 1,1--3,15
+  MdIndentedCodeBlock: 1,1--3,15
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_indented_code_tabs is test do
+               var md = """
+       some code
+
+       multi lines
+"""
+               var loc = """
+MdDocument: 1,1--3,15
+  MdIndentedCodeBlock: 1,1--3,15
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_fenced_code is test do
+               var md = """
+~~~
+some code
+
+multi lines
+~~~
+"""
+               var loc = """
+MdDocument: 1,1--5,3
+  MdFencedCodeBlock: 1,1--5,3
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_thematic_breaks is test do
+               var md = """
+***
+
+* * *
+
+*      *       *
+"""
+               var loc = """
+MdDocument: 1,1--5,5
+  MdThematicBreak: 1,1--1,3
+  MdThematicBreak: 3,1--3,5
+  MdThematicBreak: 5,1--5,5
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_html_blocks1 is test do
+               var md = """
+<p><a href="foo">bar</a></p>
+
+<div>
+       <a href="foo">bar</a>
+</div><hr />
+"""
+               var loc = """
+MdDocument: 1,1--5,12
+  MdHtmlBlock: 1,1--1,28
+  MdHtmlBlock: 3,1--5,12
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_paragraph1 is test do
+               var md = """
+foo bar baz
+
+line 1
+line 2
+
+other par
+with multiple lines
+"""
+               var loc = """
+MdDocument: 1,1--7,19
+  MdParagraph: 1,1--1,11
+    MdText: 1,1--1,11
+  MdParagraph: 3,1--4,6
+    MdText: 3,1--3,6
+    MdSoftLineBreak: 3,7--3,7
+    MdText: 4,1--4,6
+  MdParagraph: 6,1--7,19
+    MdText: 6,1--6,9
+    MdSoftLineBreak: 6,10--6,10
+    MdText: 7,1--7,19
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes is test do
+               var md = """
+> foo
+> bar
+"""
+               var loc = """
+MdDocument: 1,1--2,5
+  MdBlockQuote: 1,1--2,5
+    MdParagraph: 1,3--2,5
+      MdText: 1,3--1,5
+      MdSoftLineBreak: 1,6--1,6
+      MdText: 2,3--2,5
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_nested is test do
+               var md = """
+> foo
+> > foo
+> > bar
+"""
+               var loc = """
+MdDocument: 1,1--3,7
+  MdBlockQuote: 1,1--3,7
+    MdParagraph: 1,3--1,5
+      MdText: 1,3--1,5
+    MdBlockQuote: 2,3--3,7
+      MdParagraph: 2,5--3,7
+        MdText: 2,5--2,7
+        MdSoftLineBreak: 2,8--2,8
+        MdText: 3,5--3,7
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_headings is test do
+               var md = """
+> # Title 1
+> ## Title 2 ##
+"""
+               var loc = """
+MdDocument: 1,1--2,15
+  MdBlockQuote: 1,1--2,15
+    MdHeading: 1,3--1,11
+      MdText: 1,5--1,11
+    MdHeading: 2,3--2,15
+      MdText: 2,6--2,12
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_thematic_breaks is test do
+               var md = """
+> ***
+> * * *
+"""
+               var loc = """
+MdDocument: 1,1--2,7
+  MdBlockQuote: 1,1--2,7
+    MdThematicBreak: 1,3--1,5
+    MdThematicBreak: 2,3--2,7
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_indented_code is test do
+               var md = """
+>     line 1
+>     line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,12
+  MdBlockQuote: 1,1--2,12
+    MdIndentedCodeBlock: 1,3--2,12
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_fenced_code is test do
+               var md = """
+> ~~~
+> line 1
+> line 2
+> ~~~
+"""
+               var loc = """
+MdDocument: 1,1--4,5
+  MdBlockQuote: 1,1--4,5
+    MdFencedCodeBlock: 1,3--4,5
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_blockquotes_list is test do
+               var md = """
+> * line 1
+> * line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,10
+  MdBlockQuote: 1,1--2,10
+    MdUnorderedList: 1,3--2,10
+      MdListItem: 1,3--1,10
+        MdParagraph: 1,5--1,10
+          MdText: 1,5--1,10
+      MdListItem: 2,3--2,10
+        MdParagraph: 2,5--2,10
+          MdText: 2,5--2,10
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_unordered_lists is test do
+               var md = """
+* line 1
+* line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,8
+  MdUnorderedList: 1,1--2,8
+    MdListItem: 1,1--1,8
+      MdParagraph: 1,3--1,8
+        MdText: 1,3--1,8
+    MdListItem: 2,1--2,8
+      MdParagraph: 2,3--2,8
+        MdText: 2,3--2,8
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_ordered_lists is test do
+               var md = """
+1) line 1
+2) line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,9
+  MdOrderedList: 1,1--2,9
+    MdListItem: 1,1--1,9
+      MdParagraph: 1,4--1,9
+        MdText: 1,4--1,9
+    MdListItem: 2,1--2,9
+      MdParagraph: 2,4--2,9
+        MdText: 2,4--2,9
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_headings is test do
+               var md = """
+* # Title 1
+* ## Title 2 ##
+"""
+               var loc = """
+MdDocument: 1,1--2,15
+  MdUnorderedList: 1,1--2,15
+    MdListItem: 1,1--1,11
+      MdHeading: 1,3--1,11
+        MdText: 1,5--1,11
+    MdListItem: 2,1--2,15
+      MdHeading: 2,3--2,15
+        MdText: 2,6--2,12
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_thematic_breaks is test do
+               var md = """
+- ***
+- * * *
+"""
+               var loc = """
+MdDocument: 1,1--2,7
+  MdUnorderedList: 1,1--2,7
+    MdListItem: 1,1--1,5
+      MdThematicBreak: 1,3--1,5
+    MdListItem: 2,1--2,7
+      MdThematicBreak: 2,3--2,7
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_indented_codes is test do
+               var md = """
+-     line 1
+-     line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,12
+  MdUnorderedList: 1,1--2,12
+    MdListItem: 1,1--1,12
+      MdIndentedCodeBlock: 1,3--1,12
+    MdListItem: 2,1--2,12
+      MdIndentedCodeBlock: 2,3--2,12
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_fenced_codes is test do
+               var md = """
+- ~~~
+  line 1
+  line 2
+  ~~~
+"""
+               var loc = """
+MdDocument: 1,1--4,5
+  MdUnorderedList: 1,1--4,5
+    MdListItem: 1,1--4,5
+      MdFencedCodeBlock: 1,3--4,5
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_blockquotes is test do
+               var md = """
+- > line 1
+  > line 2
+"""
+               var loc = """
+MdDocument: 1,1--2,10
+  MdUnorderedList: 1,1--2,10
+    MdListItem: 1,1--2,10
+      MdBlockQuote: 1,3--2,10
+        MdParagraph: 1,5--2,10
+          MdText: 1,5--1,10
+          MdSoftLineBreak: 1,11--1,11
+          MdText: 2,5--2,10
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_pars is test do
+               var md = """
+* line 1
+  line 2
+
+* line 3
+"""
+               var loc = """
+MdDocument: 1,1--4,8
+  MdUnorderedList: 1,1--4,8
+    MdListItem: 1,1--2,8
+      MdParagraph: 1,3--2,8
+        MdText: 1,3--1,8
+        MdSoftLineBreak: 1,9--1,9
+        MdText: 2,3--2,8
+    MdListItem: 4,1--4,8
+      MdParagraph: 4,3--4,8
+        MdText: 4,3--4,8
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_list_nested is test do
+               var md = """
+* foo
+  * foo
+  * bar
+"""
+               var loc = """
+MdDocument: 1,1--3,7
+  MdUnorderedList: 1,1--3,7
+    MdListItem: 1,1--3,7
+      MdParagraph: 1,3--1,5
+        MdText: 1,3--1,5
+      MdUnorderedList: 2,3--3,7
+        MdListItem: 2,3--2,7
+          MdParagraph: 2,5--2,7
+            MdText: 2,5--2,7
+        MdListItem: 3,3--3,7
+          MdParagraph: 3,5--3,7
+            MdText: 3,5--3,7
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis is test do
+               var md = """
+An *emphasis* and a **strong emphasis**.
+"""
+               var loc = """
+MdDocument: 1,1--1,40
+  MdParagraph: 1,1--1,40
+    MdText: 1,1--1,3
+    MdEmphasis: 1,4--1,13
+      MdText: 1,5--1,12
+    MdText: 1,14--1,20
+    MdStrongEmphasis: 1,21--1,39
+      MdText: 1,23--1,37
+    MdText: 1,40--1,40
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis_nested is test do
+               var md = """
+Another ***emphasis***.
+"""
+               var loc = """
+MdDocument: 1,1--1,23
+  MdParagraph: 1,1--1,23
+    MdText: 1,1--1,8
+    MdEmphasis: 1,9--1,22
+      MdStrongEmphasis: 1,10--1,21
+        MdText: 1,12--1,19
+    MdText: 1,23--1,23
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis_nested2 is test do
+               var md = """
+Another ****emphasis****.
+"""
+               var loc = """
+MdDocument: 1,1--1,25
+  MdParagraph: 1,1--1,25
+    MdText: 1,1--1,8
+    MdStrongEmphasis: 1,9--1,24
+      MdStrongEmphasis: 1,11--1,22
+        MdText: 1,13--1,20
+    MdText: 1,25--1,25
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis_nested3 is test do
+               var md = """
+Another *****emphasis*****.
+"""
+               var loc = """
+MdDocument: 1,1--1,27
+  MdParagraph: 1,1--1,27
+    MdText: 1,1--1,8
+    MdEmphasis: 1,9--1,26
+      MdStrongEmphasis: 1,10--1,25
+        MdStrongEmphasis: 1,12--1,23
+          MdText: 1,14--1,21
+    MdText: 1,27--1,27
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis_bad is test do
+               var md = """
+Another ___ emphasis ___.
+"""
+               var loc = """
+MdDocument: 1,1--1,25
+  MdParagraph: 1,1--1,25
+    MdText: 1,1--1,25
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_emphasis_bad2 is test do
+               var md = """
+Another **emphasis.
+"""
+               var loc = """
+MdDocument: 1,1--1,19
+  MdParagraph: 1,1--1,19
+    MdText: 1,1--1,19
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_code is test do
+               var md = """
+A `code` and another ``one``.
+"""
+               var loc = """
+MdDocument: 1,1--1,29
+  MdParagraph: 1,1--1,29
+    MdText: 1,1--1,2
+    MdCode: 1,3--1,8
+    MdText: 1,9--1,21
+    MdCode: 1,22--1,28
+    MdText: 1,29--1,29
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_code_bad is test do
+               var md = """
+A `code and another ``one``.
+"""
+               var loc = """
+MdDocument: 1,1--1,28
+  MdParagraph: 1,1--1,28
+    MdText: 1,1--1,20
+    MdCode: 1,21--1,27
+    MdText: 1,28--1,28
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_autolink is test do
+               var md = """
+An <http://autolink>.
+"""
+               var loc = """
+MdDocument: 1,1--1,21
+  MdParagraph: 1,1--1,21
+    MdText: 1,1--1,3
+    MdLink: 1,4--1,20
+      MdText: 1,5--1,19
+    MdText: 1,21--1,21
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_autolink_bad is test do
+               var md = """
+An http://autolink>.
+"""
+               var loc = """
+MdDocument: 1,1--1,20
+  MdParagraph: 1,1--1,20
+    MdText: 1,1--1,20
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_autolink_bad2 is test do
+               var md = """
+An <http://autolink.
+"""
+               var loc = """
+MdDocument: 1,1--1,20
+  MdParagraph: 1,1--1,20
+    MdText: 1,1--1,20
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_automail is test do
+               var md = """
+An <me.foo+@bar.baz>.
+"""
+               var loc = """
+MdDocument: 1,1--1,21
+  MdParagraph: 1,1--1,21
+    MdText: 1,1--1,3
+    MdLink: 1,4--1,20
+      MdText: 1,5--1,19
+    MdText: 1,21--1,21
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link is test do
+               var md = """
+A [link](url/).
+"""
+               var loc = """
+MdDocument: 1,1--1,15
+  MdParagraph: 1,1--1,15
+    MdText: 1,1--1,2
+    MdLink: 1,3--1,14
+      MdText: 1,4--1,7
+    MdText: 1,15--1,15
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_with_title is test do
+               var md = """
+A [link](url/ "title").
+"""
+               var loc = """
+MdDocument: 1,1--1,23
+  MdParagraph: 1,1--1,23
+    MdText: 1,1--1,2
+    MdLink: 1,3--1,22
+      MdText: 1,4--1,7
+    MdText: 1,23--1,23
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_with_content is test do
+               var md = """
+A [`code` link](url/).
+"""
+               var loc = """
+MdDocument: 1,1--1,22
+  MdParagraph: 1,1--1,22
+    MdText: 1,1--1,2
+    MdLink: 1,3--1,21
+      MdCode: 1,4--1,9
+      MdText: 1,10--1,14
+    MdText: 1,22--1,22
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_bad is test do
+               var md = """
+A [link](url/.
+"""
+               var loc = """
+MdDocument: 1,1--1,14
+  MdParagraph: 1,1--1,14
+    MdText: 1,1--1,14
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_image is test do
+               var md = """
+A ![img](url/).
+"""
+               var loc = """
+MdDocument: 1,1--1,15
+  MdParagraph: 1,1--1,15
+    MdText: 1,1--1,2
+    MdImage: 1,3--1,14
+      MdText: 1,5--1,7
+    MdText: 1,15--1,15
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_image_bad is test do
+               var md = """
+A ![img](url/.
+"""
+               var loc = """
+MdDocument: 1,1--1,14
+  MdParagraph: 1,1--1,14
+    MdText: 1,1--1,14
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_ref is test do
+               var md = """
+A [link][].
+
+[link]: url/
+"""
+               var loc = """
+MdDocument: 1,1--1,11
+  MdParagraph: 1,1--1,11
+    MdText: 1,1--1,2
+    MdLink: 1,3--1,10
+      MdText: 1,4--1,7
+    MdText: 1,11--1,11
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_ref2 is test do
+               var md = """
+A [foo][link].
+
+[link]: url/
+"""
+               var loc = """
+MdDocument: 1,1--1,14
+  MdParagraph: 1,1--1,14
+    MdText: 1,1--1,2
+    MdLink: 1,3--1,13
+      MdText: 1,4--1,6
+    MdText: 1,14--1,14
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_link_ref_bad is test do
+               var md = """
+A [foo][link2].
+
+[link]: url/
+"""
+               var loc = """
+MdDocument: 1,1--1,15
+  MdParagraph: 1,1--1,15
+    MdText: 1,1--1,15
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_html is test do
+               var md = """
+An <br /> break line.
+"""
+               var loc = """
+MdDocument: 1,1--1,21
+  MdParagraph: 1,1--1,21
+    MdText: 1,1--1,3
+    MdHtmlInline: 1,4--1,9
+    MdText: 1,10--1,21
+"""
+               assert md_to_loc(md) == loc
+       end
+
+
+       fun test_inline_html2 is test do
+               var md = """
+An <a href="link">*emph*</a>.
+"""
+               var loc = """
+MdDocument: 1,1--1,29
+  MdParagraph: 1,1--1,29
+    MdText: 1,1--1,3
+    MdHtmlInline: 1,4--1,18
+    MdEmphasis: 1,19--1,24
+      MdText: 1,20--1,23
+    MdHtmlInline: 1,25--1,28
+    MdText: 1,29--1,29
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_escape is test do
+               var md = """
+A text with \\"escaped chars\\".
+"""
+               var loc = """
+MdDocument: 1,1--1,30
+  MdParagraph: 1,1--1,30
+    MdText: 1,1--1,30
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_soft_break is test do
+               var md = """
+A text with
+a soft break.
+"""
+               var loc = """
+MdDocument: 1,1--2,13
+  MdParagraph: 1,1--2,13
+    MdText: 1,1--1,11
+    MdSoftLineBreak: 1,12--1,12
+    MdText: 2,1--2,13
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_soft_break2 is test do
+               var md = """A text with \na hard break.\n"""
+               var loc = """
+MdDocument: 1,1--2,13
+  MdParagraph: 1,1--2,13
+    MdText: 1,1--1,11
+    MdSoftLineBreak: 1,12--1,13
+    MdText: 2,1--2,13
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_hard_break is test do
+               var md = """
+A text with\\
+a hard break.
+"""
+               var loc = """
+MdDocument: 1,1--2,13
+  MdParagraph: 1,1--2,13
+    MdText: 1,1--1,11
+    MdHardLineBreak: 1,12--1,13
+    MdText: 2,1--2,13
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_inline_hard_break2 is test do
+               var md = """A text with  \na hard break.\n"""
+               var loc = """
+MdDocument: 1,1--2,13
+  MdParagraph: 1,1--2,13
+    MdText: 1,1--1,11
+    MdHardLineBreak: 1,12--1,14
+    MdText: 2,1--2,13
+"""
+               assert md_to_loc(md) == loc
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_man.nit b/lib/markdown2/tests/test_markdown_man.nit
new file mode 100644 (file)
index 0000000..e64dd2a
--- /dev/null
@@ -0,0 +1,386 @@
+# 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 manpage
+module test_markdown_man is test
+
+import test_markdown
+import markdown_man_rendering
+
+# Abstract test class that defines the test methods for Man rendering
+abstract class TestMarkdownMan
+       super TestMarkdown
+
+       # Man renderer used in tests
+       var man_renderer = new ManRenderer
+
+       # Render the `md` string as Manpage format
+       fun md_to_man(md: String): String do
+               var node = parse_md(md)
+               return man_renderer.render(node)
+       end
+end
+
+class TestManRendering
+       super TestMarkdownMan
+       test
+
+       fun test_headings is test do
+               var md = """# title1\n## title2\n### title3\n#### title4\n##### title5\n###### title6\n"""
+               var man = """.SH title1\n.SS title2\n.TP\ntitle3\n.TP\ntitle4\n.TP\ntitle5\n.TP\ntitle6\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_bquotes is test do
+               var md = """> line 1\n> line 2\n\n> line 3\n>line 4"""
+               var man = """.RS\nline 1\nline 2\n.RE\n.RS\nline 3\nline 4\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_breaks is test do
+               var md = """* * *"""
+               var man = """***\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_indented_code is test do
+               var md = """\tline 1\n\tline 2\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_fenced_code is test do
+               var md = """~~~\nline 1\nline 2\n~~~\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_escaped_code is test do
+               var md = """\tline - 1\n\tline - 2\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ \\-\\ 1\nline\\ \\-\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_unordered_list is test do
+               var md = """* line 1\n* line 2\n"""
+               var man = """.RS\n.IP \\[bu] 3\nline 1\n.IP \\[bu] 3\nline 2\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_ordered_list is test do
+               var md = """2) line 1\n3) line 2\n"""
+               var man = """.RS\n.IP "2." 3\nline 1\n.IP "3." 3\nline 2\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_paragraph is test do
+               var md = """line 1\nline 2\n\nline 3\nline 4\n"""
+               var man = """\nline 1\nline 2\n\nline 3\nline 4\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_escaped_text is test do
+               var md = """foo - bar\n"""
+               var man = """\nfoo \\- bar\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_inline_code is test do
+               var md = """`foo - bar`\n"""
+               var man = """\n\\f[C]foo\\ \\-\\ bar\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_emphasis is test do
+               var md = """*foo*\n"""
+               var man = """\n\\f[I]foo\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_strong_emphasis is test do
+               var md = """**foo**\n"""
+               var man = """\n\\f[B]foo\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_link is test do
+               var md = """[foo](url "title")\n"""
+               var man = """\nfoo (url title)\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_image is test do
+               var md = """![foo](url "title")\n"""
+               var man = """\nfoo (url title)\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_full_document is test do
+
+               var md = """
+# NAME
+
+nitdoc - generates HTML pages of API documentation from Nit source files.
+
+# SYNOPSIS
+
+nitdoc [*options*]... FILE...
+
+# DESCRIPTION
+
+`nitdoc` takes one or more modules and generate HTML pages of API documentation for these modules and their imported modules.
+
+The documentation is extracted from the comments found above the definition of modules, classes, and properties.
+
+Internally, `nitdoc` relies on the presence of the `dot` command from the [graphviz] project.
+If the dot program is not present or not found, no image of hierarchies are generated.
+See option `--no-dot`.
+
+The documentation of the Nit [standard library] is generated with this tool.
+
+  [graphviz]: http://www.graphviz.org
+  [standard library]: http://nitlanguage.org/doc/stdlib
+
+# DOCUMENTATION FORMAT
+
+The format of the documentation is a dialect of [markdown] that allows GitHub fences (`~~~`).
+
+Code blocks are interpreted as snippets of Nit programs and intended to be used as examples of code.
+When these code snippets are valid, executable and contain at least and `assert` clause, they could be automatically executed and verified.
+See `nitunit(1)` for details.
+
+  [markdown]: http://daringfireball.net/projects/markdown
+
+# OPTIONS
+
+### `-d`, `--dir`
+Output directory.
+
+Where the HTML files are generated.
+
+By default, the directory is named `doc`.
+
+### `--source`
+Format to link source code.
+
+The format string is used to generated links to some parts of the source-code.
+Use `%f` for filename, `%l` for first line, and `%L` for last line.
+
+For instance, the [standard library] use the following value to link to files in GitHub:
+
+    "https://github.com/nitlang/nit/blob/$(git rev-parse HEAD)/%f#L%l-%L"
+
+Here, the `git rev-parse HEAD` is used to link to the current snapshot revision of the file.
+
+### `--no-attributes`
+Ignore the attributes.
+
+Note: In Nit, attributes are private. Therefore, this option is only useful
+when combined with `--private`.
+
+### `--no-dot`
+Do not generate graphs with graphviz.
+
+### `--private`
+Also generate private API.
+
+## CUSTOMIZATION
+
+### `--share-dir`
+Directory containing tools assets.
+
+By default `$NIT_DIR/share/nitdoc/` is used.
+
+### `--shareurl`
+Use shareurl instead of copy shared files.
+
+By default, assets from the sharedir a copied into the output directory and referred with a relative path in the generated files.
+With this option, the assets are not copied and the given URL of path is used in the generated files to locate assets.
+
+### `--custom-title`
+Custom title for homepage.
+
+### `--custom-footer-text`
+Custom footer text.
+
+### `--custom-overview-text`
+Custom intro text for homepage.
+
+### `--custom-brand`
+Custom link to external site.
+
+## SERVICES
+
+### `--github-upstream`
+Git branch where edited commits will be pulled into (ex: user:repo:branch).
+
+### `--github-base-sha1`
+Git sha1 of base commit used to create pull request.
+
+### `--github-gitdir`
+Git working directory used to resolve path name (ex: /home/me/myproject/).
+
+### `--piwik-tracker`
+Piwik tracker URL (ex: `nitlanguage.org/piwik/`).
+
+### `--piwik-site-id`
+Piwik site ID.
+
+## TESTING
+
+### `--test`
+Print test data (metrics and structure).
+
+### `--no-render`
+Do not render HTML files.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
+"""
+
+               var man = """
+.SH NAME
+
+nitdoc \\- generates HTML pages of API documentation from Nit source files.
+.SH SYNOPSIS
+
+nitdoc [\\f[I]options\\f[]]... FILE...
+.SH DESCRIPTION
+
+\\f[C]nitdoc\\f[] takes one or more modules and generate HTML pages of API documentation for these modules and their imported modules.
+
+The documentation is extracted from the comments found above the definition of modules, classes, and properties.
+
+Internally, \\f[C]nitdoc\\f[] relies on the presence of the \\f[C]dot\\f[] command from the graphviz (http://www.graphviz.org) project.
+If the dot program is not present or not found, no image of hierarchies are generated.
+See option \\f[C]\\-\\-no\\-dot\\f[].
+
+The documentation of the Nit standard library (http://nitlanguage.org/doc/stdlib) is generated with this tool.
+.SH DOCUMENTATION FORMAT
+
+The format of the documentation is a dialect of markdown (http://daringfireball.net/projects/markdown) that allows GitHub fences (\\f[C]~~~\\f[]).
+
+Code blocks are interpreted as snippets of Nit programs and intended to be used as examples of code.
+When these code snippets are valid, executable and contain at least and \\f[C]assert\\f[] clause, they could be automatically executed and verified.
+See \\f[C]nitunit(1)\\f[] for details.
+.SH OPTIONS
+.TP
+\\f[C]\\-d\\f[], \\f[C]\\-\\-dir\\f[]
+
+Output directory.
+
+Where the HTML files are generated.
+
+By default, the directory is named \\f[C]doc\\f[].
+.TP
+\\f[C]\\-\\-source\\f[]
+
+Format to link source code.
+
+The format string is used to generated links to some parts of the source\\-code.
+Use \\f[C]%f\\f[] for filename, \\f[C]%l\\f[] for first line, and \\f[C]%L\\f[] for last line.
+
+For instance, the standard library (http://nitlanguage.org/doc/stdlib) use the following value to link to files in GitHub:
+.RS
+.nf
+\\f[C]
+"https://github.com/nitlang/nit/blob/$(git\\ rev\\-parse\\ HEAD)/%f#L%l\\-%L"
+\\f[]
+.fi
+.RE
+
+Here, the \\f[C]git\\ rev\\-parse\\ HEAD\\f[] is used to link to the current snapshot revision of the file.
+.TP
+\\f[C]\\-\\-no\\-attributes\\f[]
+
+Ignore the attributes.
+
+Note: In Nit, attributes are private. Therefore, this option is only useful
+when combined with \\f[C]\\-\\-private\\f[].
+.TP
+\\f[C]\\-\\-no\\-dot\\f[]
+
+Do not generate graphs with graphviz.
+.TP
+\\f[C]\\-\\-private\\f[]
+
+Also generate private API.
+.SS CUSTOMIZATION
+.TP
+\\f[C]\\-\\-share\\-dir\\f[]
+
+Directory containing tools assets.
+
+By default \\f[C]$NIT_DIR/share/nitdoc/\\f[] is used.
+.TP
+\\f[C]\\-\\-shareurl\\f[]
+
+Use shareurl instead of copy shared files.
+
+By default, assets from the sharedir a copied into the output directory and referred with a relative path in the generated files.
+With this option, the assets are not copied and the given URL of path is used in the generated files to locate assets.
+.TP
+\\f[C]\\-\\-custom\\-title\\f[]
+
+Custom title for homepage.
+.TP
+\\f[C]\\-\\-custom\\-footer\\-text\\f[]
+
+Custom footer text.
+.TP
+\\f[C]\\-\\-custom\\-overview\\-text\\f[]
+
+Custom intro text for homepage.
+.TP
+\\f[C]\\-\\-custom\\-brand\\f[]
+
+Custom link to external site.
+.SS SERVICES
+.TP
+\\f[C]\\-\\-github\\-upstream\\f[]
+
+Git branch where edited commits will be pulled into (ex: user:repo:branch).
+.TP
+\\f[C]\\-\\-github\\-base\\-sha1\\f[]
+
+Git sha1 of base commit used to create pull request.
+.TP
+\\f[C]\\-\\-github\\-gitdir\\f[]
+
+Git working directory used to resolve path name (ex: /home/me/myproject/).
+.TP
+\\f[C]\\-\\-piwik\\-tracker\\f[]
+
+Piwik tracker URL (ex: \\f[C]nitlanguage.org/piwik/\\f[]).
+.TP
+\\f[C]\\-\\-piwik\\-site\\-id\\f[]
+
+Piwik site ID.
+.SS TESTING
+.TP
+\\f[C]\\-\\-test\\f[]
+
+Print test data (metrics and structure).
+.TP
+\\f[C]\\-\\-no\\-render\\f[]
+
+Do not render HTML files.
+.SH SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from http://nitlanguage.org (http://nitlanguage.org)
+"""
+               assert md_to_man(md) == man
+       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
diff --git a/lib/markdown2/tests/test_markdown_wikilinks.nit b/lib/markdown2/tests/test_markdown_wikilinks.nit
new file mode 100644 (file)
index 0000000..8721e14
--- /dev/null
@@ -0,0 +1,180 @@
+# 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 Wikilinks mode
+module test_markdown_wikilinks is test
+
+import test_markdown
+import test_markdown_location
+import test_markdown_md
+import test_markdown_man
+import test_markdown_latex
+
+redef class TestMarkdown
+       redef var md_parser do
+               var parser = super
+               parser.wikilinks_mode = true
+               return parser
+       end
+end
+
+class TestWikilinksLocation
+       super TestMarkdownLocation
+       test
+
+       fun test_wikilinks1 is test do
+               var md = """
+A [[wiki link]] and text.
+"""
+               var loc = """
+MdDocument: 1,1--1,25
+  MdParagraph: 1,1--1,25
+    MdText: 1,1--1,2
+    MdWikilink: 1,3--1,15
+      MdText: 1,5--1,13
+    MdText: 1,16--1,25
+"""
+               assert md_to_loc(md) == loc
+       end
+
+       fun test_wikilinks2 is test do
+               var md = """
+A [[wiki: link | with: more, args: end]] and text.
+"""
+               var loc = """
+MdDocument: 1,1--1,50
+  MdParagraph: 1,1--1,50
+    MdText: 1,1--1,2
+    MdWikilink: 1,3--1,40
+      MdText: 1,5--1,38
+    MdText: 1,41--1,50
+"""
+               assert md_to_loc(md) == loc
+       end
+
+end
+
+class TestWikilinksHtml
+       super TestMarkdownHtml
+       test
+
+       fun test_wikilinks1 is test do
+               var md = """[[foo]]\n"""
+               var html = """<p><wiki link="foo">foo</wiki></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilinks2 is test do
+               var md = """[[foo | bar baz]]\n"""
+               var html = """<p><wiki link="bar%20baz">foo</wiki></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilinks3 is test do
+               var md = """This is a [[link]] and this is another [[one]].\n"""
+               var html = """<p>This is a <wiki link="link">link</wiki> and this is another <wiki link="one">one</wiki>.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilinks4 is test do
+               var md = """[[very: complex | link: with, more: options]]\n"""
+               var html = """<p><wiki link="link:%20with,%20more:%20options">very: complex</wiki></p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilink_bad1 is test do
+               var md = """Not a [wikilink]].\n"""
+               var html = """<p>Not a [wikilink]].</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilink_bad2 is test do
+               var md = """Not a [[wikilink].\n"""
+               var html = """<p>Not a [[wikilink].</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilink_bad3 is test do
+               var md = """Not a ![[wikilink]].\n"""
+               var html = """<p>Not a ![[wikilink]].</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_wikilink_bad4 is test do
+               var md = """Not a [wikilink].\n"""
+               var html = """<p>Not a [wikilink].</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_link is test do
+               var md = """A standard [link](url).\n"""
+               var html = """<p>A standard <a href="url">link</a>.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_image is test do
+               var md = """A standard ![image](url).\n"""
+               var html = """<p>A standard <img src="url" alt="image" />.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_link_ref1 is test do
+               var md = """A standard [link definition].\n\n[link definition]: url\n"""
+               var html = """<p>A standard <a href="url">link definition</a>.</p>\n"""
+               assert md_to_html(md) == html
+       end
+
+       fun test_link_ref2 is test do
+               var md = """[[wikilinks]] are not \n\n[[link definition]]: url\n"""
+               var html = """<p><wiki link="wikilinks">wikilinks</wiki> are not</p>\n<p><wiki link="link%20definition">link definition</wiki>: url</p>\n"""
+               assert md_to_html(md) == html
+       end
+end
+
+class TestWikilinksMd
+       super TestMarkdownMd
+       test
+
+       fun test_wikilinks_md1 is test do
+               var md = """[[foo]]\n"""
+               assert md_to_md(md) == md
+       end
+
+       fun test_wikilinks_md2 is test do
+               var md = """[[foo: bar | baz: b, c: d]]\n"""
+               assert md_to_md(md) == md
+       end
+end
+
+class TestWikilinksMan
+       super TestMarkdownMan
+       test
+
+       fun test_wikilinks_man is test do
+               var md = """[[foo]]\n"""
+               var man = """\n(foo)\n"""
+               assert md_to_man(md) == man
+       end
+end
+
+class TestWikilinksLatex
+       super TestMarkdownLatex
+       test
+
+       fun test_wikilinks_latex is test do
+               var md = """[[foo]]\n"""
+               var tex = """\\texttt{foo}\n"""
+               assert md_to_tex(md) == tex
+       end
+end