}
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"
# 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
clean:
make -C nitmd clean
+ make -C nitmd2 clean
make -C txtmark clean
make -C markdown4j clean
make -C pandoc clean
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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 = "&"
+ else if c == '<' then
+ sub = "<"
+ else if c == '>' then
+ sub = ">"
+ else if c == '"' then
+ sub = """
+ 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 "&"
+ 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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)
--- /dev/null
+[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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&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><http://foo.bar/baz bim></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><foo+@bar.example.com></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test581 is test do
+ var md = """<>\n"""
+ var html = """<p><></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test582 is test do
+ var md = """< http://foo.bar >\n"""
+ var html = """<p>< http://foo.bar ></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test583 is test do
+ var md = """<m:abc>\n"""
+ var html = """<p><m:abc></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test584 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 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
--- /dev/null
+# 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>!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~</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<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"</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
--- /dev/null
+# 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
--- /dev/null
+# 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>> # Foo\n> bar\n> 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
--- /dev/null
+# 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><a href="</code>">`</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><http://foo.bar.</code>baz>`</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
--- /dev/null
+# 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*"foo"*</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_"foo"_</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_"bb"_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**"foo"**</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__"foo"__</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 "<em>bar</em>" 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
--- /dev/null
+# 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 = """  &x; &#; &#x;\n�\n&#abcdef0;\n&ThisIsNotDefined; &hi?;\n"""
+ var html = """<p>&nbsp &x; &#; &#x;\n&#987654321;\n&#abcdef0;\n&ThisIsNotDefined; &hi?;</p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test308 is test do
+ var md = """©\n"""
+ var html = """<p>&copy</p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test309 is test do
+ var md = """&MadeUpEntity;\n"""
+ var html = """<p>&MadeUpEntity;</p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test310 is test do
+ var md = """<a href="öö.html">\n"""
+ var html = """<a href="öö.html">\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test314 is test do
+ var md = """`föö`\n"""
+ var html = """<p><code>f&ouml;&ouml;</code></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test315 is test do
+ var md = """ föfö\n"""
+ var html = """<pre><code>f&ouml;f&ouml;\n</code></pre>\n"""
+ assert md_to_html(md) == html
+ end
+end
--- /dev/null
+# 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><\n >\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><\n >\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
--- /dev/null
+# 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
--- /dev/null
+# 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><!-- foo -->\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><div>\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><td>\n Hi\n</td>\n</code></pre>\n </tr>\n</table>\n"""
+ assert md_to_html(md) == html
+ end
+end
--- /dev/null
+# 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 & 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 & 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 & 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 "title"</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
--- /dev/null
+# 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><a/>\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
--- /dev/null
+# 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
--- /dev/null
+# 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"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 "title" 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>"title" 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 "title"\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
--- /dev/null
+# 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 \\""")\n"""
+ var html = """<p><a href="/url" title="title """>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 "title "and" title")</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 "and" 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
--- /dev/null
+# 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 > 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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><33> <__></p>\n"""
+ assert md_to_html(md) == html
+ end
+
+ fun test593 is test do
+ var md = """<a h*#ref="hi">\n"""
+ var html = """<p><a h*#ref="hi"></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><a href="hi'> <a href=hi'></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>< a><\nfoo><bar/ >\n<foo bar=baz\nbim!bop /></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><a href='bar'title=title></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></a href="foo"></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 <!-- not a comment -- two hyphens --></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 <!--> foo --></p>\n<p>foo <!-- foo---></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="ö">\n"""
+ var html = """<p>foo <a href="ö"></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><a href="""></p>\n"""
+ assert md_to_html(md) == html
+ end
+end
--- /dev/null
+# 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><a title="a lot</h2>\n<p>of dashes"/></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>> 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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">
+ © 2004 Foo Corporation
+ </div>
+"""
+ var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ &copy; 2004 Foo Corporation
+</div>
+</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">
+ © 2004 Foo Corporation
+</div>
+~~~
+"""
+ var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ &copy; 2004 Foo Corporation
+</div>
+</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">
+ © 2004 Foo Corporation
+</div>
+```
+"""
+ var html = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ &copy; 2004 Foo Corporation
+</div>
+</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 "Hello World!"
+</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 "Hello"
+</code></pre>
+<pre><code>print "World"
+</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 "Hello"
+</code></pre>
+<pre><code>print "World"
+</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("echo $input | $markdown_script");
+</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><code goes here>
+</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
--- /dev/null
+# 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&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&T has an ampersand in their name.</p>
+<p>AT&T is another way to write it.</p>
+<p>This & that.</p>
+<p>4 < 5.</p>
+<p>6 > 5.</p>
+<p>Here's a <a href="http://example.com/?foo=1&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&T">AT&T</a>.</p>
+<p>Here's an inline <a href="/script?foo=1&bar=2">link</a>.</p>
+<p>Here's an inline <a href="/script?foo=1&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&bar=2">http://example.com/?foo=1&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><http://example.com/></code></p>
+<pre><code>or here: <http://example.com/>
+</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: ></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: \>
+
+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>\></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 "working";
+}
+</code></pre>
+<p>Or:</p>
+<pre><code>sub status {
+ return "working";
+}
+</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><test a="</code> content of attribute <code>"></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><div>
+ foo
+</div>
+</code></pre>
+<p>As should this:</p>
+<pre><code><div>foo</div>
+</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><!-- Comment -->
+</code></pre>
+<p>Just plain comment, with trailing spaces on the line:</p>
+<!-- foo -->
+<p>Code:</p>
+<pre><code><hr />
+</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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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 < , +1 if > 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><blink></code> tags.</p>\n"
+ assert md_to_html(md) == html
+ end
+
+ fun test_inlines_span_code5 is test do
+ var md = "`—` is the decimal-encoded equivalent of `—`."
+ var html = "<p><code>&#8212;</code> is the decimal-encoded equivalent of <code>&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 & 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 "quotes" inside">bar</a>.</p>
+<p>Foo <a href="/url/" title="Title with "quotes" 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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