From 47b42b9e9ea72be66a0c4121a18fc929e6f2de46 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Fri, 18 May 2018 13:52:06 -0400 Subject: [PATCH 01/17] lib/markdown2: introduce markdown AST nodes Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_ast.nit | 487 +++++++++++++++++++++++++++++++++ lib/markdown2/package.ini | 12 + 2 files changed, 499 insertions(+) create mode 100644 lib/markdown2/markdown_ast.nit create mode 100644 lib/markdown2/package.ini diff --git a/lib/markdown2/markdown_ast.nit b/lib/markdown2/markdown_ast.nit new file mode 100644 index 0000000000..3de2196f93 --- /dev/null +++ b/lib/markdown2/markdown_ast.nit @@ -0,0 +1,487 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown AST representation +module markdown_ast + +# An abstract node +abstract class MdNode + + # Node location in original markdown + var location: MdLocation + + # Node parent + var parent: nullable MdNode = null is writable + + # First child + var first_child: nullable MdNode = null is writable + + # Last child + var last_child: nullable MdNode = null is writable + + # Previous node + var prev: nullable MdNode = null is writable + + # Next node + var next: nullable MdNode = null is writable + + # Children nodes of `self` + fun children: Array[MdNode] do + var nodes = new Array[MdNode] + + var node = first_child + while node != null do + nodes.add node + node = node.next + end + + return nodes + end + + # Append a child to `self` + fun append_child(child: MdNode) do + child.unlink + child.parent = self + if last_child != null then + last_child.as(not null).next = child + child.prev = last_child + last_child = child + else + first_child = child + last_child = child + end + end + + # Prepend a child to `self` + fun prepend_child(child: MdNode) do + child.unlink + child.parent = self + if first_child != null then + first_child.as(not null).prev = child + child.next = first_child + first_child = child + else + first_child = child + last_child = child + end + end + + # Unlink `self` from its `parent` + fun unlink do + if prev != null then + prev.as(not null).next = next + else if parent != null then + parent.as(not null).first_child = next + end + if next != null then + next.as(not null).prev = prev + else if parent != null then + parent.as(not null).last_child = prev + end + parent = null + next = null + prev = null + end + + # Insert `sibling` after `self`. + fun insert_after(sibling: MdNode) do + sibling.unlink + sibling.next = next + if sibling.next != null then + sibling.next.as(not null).prev = sibling + end + sibling.prev = self + next = sibling + sibling.parent = parent + if sibling.next == null then + sibling.parent.as(not null).last_child = sibling + end + end + + # Insert `sibling` before `self`. + fun insert_before(sibling: MdNode) do + sibling.unlink + sibling.prev = prev + if sibling.prev != null then + sibling.prev.as(not null).next = sibling + end + sibling.next = self + prev = sibling + sibling.parent = parent + if sibling.prev == null then + sibling.parent.as(not null).first_child = sibling + end + end + + # Visit all children or `self` + fun visit_all(v: MdVisitor) do + var node = first_child + while node != null do + var next = node.next + v.visit(node) + node = next + end + end + + redef fun to_s do return "{super}\{{to_s_attrs}\}" + + # Returns `self` attributes as a String + # + # Mainly used for debug purposes. + fun to_s_attrs: String do return "loc: {location}" + + # Print `self` AST + fun debug do + var v = new MdASTPrinter + v.enter_visit(self) + end +end + +# A visitor for Markdown AST +interface MdVisitor + + # Start visiting `node` + fun enter_visit(node: MdNode) do visit(node) + + # Visit `node` + # + # Method to define in specific visitor. + # It should not be called directly but used by `enter_visit`. + protected fun visit(node: MdNode) is abstract +end + +# Print the AST content +class MdASTPrinter + super MdVisitor + + # Current indent level + var indent = 0 + + # Visit `self` to print the AST content + fun print_ast(node: MdNode) do + print "{" " * indent}{node}" + indent += 1 + node.visit_all(self) + indent -= 1 + end + + redef fun visit(node) do print_ast(node) +end + +# Blocks + +# An abstract markdown block +abstract class MdBlock + super MdNode + + redef fun parent do return super + + # Can this block contain other blocks? + var is_container = false + + # Can this block contain `block`? + fun can_contain(block: MdBlock): Bool do return false + + # Parents of blocks can only be blocks + redef fun parent=(node) do + assert parent == null or parent isa MdBlock else + print "Parent of block must also be block." + end + super(node) + end +end + +# A Markdown document +class MdDocument + super MdBlock + + redef var is_container = true + + redef fun can_contain(block) do return true +end + +# A block quote +class MdBlockQuote + super MdBlock + + redef var is_container = true + + redef fun can_contain(block) do return true +end + +# A block of code (indented or fenced) +abstract class MdCodeBlock + super MdBlock + + # Literal content + var literal: nullable String = null is writable + + # Fence info / meta + var info: nullable String = null is writable + + redef fun to_s_attrs do return "{super}, info={info or else "null"}, literal={literal or else "null"}" +end + +# A block code that starts with an indent +class MdIndentedCodeBlock + super MdCodeBlock + + # Does this block use tabs instead of spaces? + var use_tabs: Bool + + redef fun to_s_attrs do return "{super}, use_tabs={use_tabs}" +end + +# A code block that starts with a fence +class MdFencedCodeBlock + super MdCodeBlock + + # Fence character + var fence_char: Char + + # Fence length + var fence_length: Int + + # Fence indentation level + var fence_indent: Int + + redef fun to_s_attrs do return "{super}, fence_char={fence_char}, fence_length={fence_length}, fence_indent={fence_indent}" +end + +# A heading +class MdHeading + super MdBlock + + # Heading level (from 1 to 6) + var level: Int + + # Is this heading in the setext format in the original source? + var is_setext = false is optional + + # Does this heading has an atx trailing in the original source? + var has_atx_trailing = false is optional + + redef fun to_s_attrs do return "{super}, level={level}" +end + +# An html block +class MdHtmlBlock + super MdBlock + + # Literal content + var literal: nullable String = null is writable + + redef fun to_s_attrs do return "{super}, literal={literal or else "null"}" +end + +# An ordered or unordered list block +abstract class MdListBlock + super MdBlock + + # Does this list contains line breaks? + var is_tight: Bool = false is writable + + redef var is_container = true + + redef fun can_contain(block) do return block isa MdListItem + + redef fun to_s_attrs do return "{super}, is_tight={is_tight}" +end + +# An ordered or unordered list item block +class MdListItem + super MdBlock + + redef var is_container = true + + redef fun can_contain(block) do return true +end + +# An ordered list block +class MdOrderedList + super MdListBlock + + # List starting number + var start_number: Int + + # List number delimiter + var delimiter: Char + + redef fun to_s_attrs do return "{super}, start_number={start_number}, delimiter={delimiter}" +end + +# An unordered list +class MdUnorderedList + super MdListBlock + + # List bullet marker + var bullet_marker: Char + + redef fun to_s_attrs do return "{super}, bullet_marker={bullet_marker}" +end + +# A paragraph block +class MdParagraph + super MdBlock + + # Is this paragraph in a list? + fun is_in_list: Bool do + var parent = self.parent + return parent != null and parent.parent isa MdListBlock + end + + # Is this paragraph in a tight list? + fun is_in_tight_list: Bool do + var parent = self.parent + if parent == null then return false + var gramps = parent.parent + return gramps isa MdListBlock and gramps.is_tight + end +end + +# A ruler +class MdThematicBreak + super MdBlock + + # Thematic break pattern used in markdown source + var original_pattern: String +end + +# Inline nodes + +# A line break (soft or hard) +abstract class MdLineBreak + super MdNode +end + +# A hardline break (`\\n` or ` \n`) +class MdHardLineBreak + super MdLineBreak + + # Does this line break used a backslash in the original source? + var has_backslash: Bool +end + +# A soft line breack (`\r` or `\n`) +class MdSoftLineBreak + super MdLineBreak +end + +# An inline code string +class MdCode + super MdNode + + # Emphasis delimiter + var delimiter: String + + # Literal content + var literal: String is writable + + redef fun to_s_attrs do return "{super}, literal={literal}" +end + +# A node that users delimiters in the Markdown form +# +# For example the emphasis: `*bold*`. +abstract class MdDelimited + super MdNode + + # Emphasis delimiter + var delimiter: String + + # Opening delimiter + fun opening_delimiter: String do return delimiter + + # Closing delimiter + fun closing_delimiter: String do return delimiter + + redef fun to_s_attrs do return "{super}, delimiter={delimiter}" +end + +# An emphasis +class MdEmphasis + super MdDelimited +end + +# A strong emphasis token +class MdStrongEmphasis + super MdDelimited +end + +# An inlined html string +class MdHtmlInline + super MdNode + + # Literal content + var literal: String is writable + + redef fun to_s_attrs do return "{super}, literal={literal}" +end + +# A link or image +abstract class MdLinkOrImage + super MdNode + + # Link destination + var destination: String is writable + + # Link title + var title: nullable String is writable + + # Is the `destination` between pointy brackets `` + var has_brackets = false is writable + + redef fun to_s_attrs do return "{super}, destination={destination}, title={title or else "null"}" +end + +# An image +class MdImage + super MdLinkOrImage +end + +# A link +class MdLink + super MdLinkOrImage + + # Is this link an autolink? + var is_autolink = false is optional, writable +end + +# A raw text token +class MdText + super MdNode + + # Literal content + var literal: String is writable + + redef fun to_s_attrs do return "{super}, literal={literal}" +end + +# MdNode location in the Markdown input +class MdLocation + + # Starting line number (starting from 1). + var line_start: Int is writable + + # Starting column number (starting from 1). + var column_start: Int is writable + + # Stopping line number (starting from 1). + var line_end: Int is writable + + # Stopping column number (starting from 1). + var column_end: Int is writable + + redef fun to_s do return "{line_start},{column_start}--{line_end},{column_end}" +end diff --git a/lib/markdown2/package.ini b/lib/markdown2/package.ini new file mode 100644 index 0000000000..a138d9351f --- /dev/null +++ b/lib/markdown2/package.ini @@ -0,0 +1,12 @@ +[package] +name=markdown2 +tags=format,lib +maintainer=Alexandre Terrasa +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 From fe8254ab40d48ae0906db787a6e10698cdae53e5 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:51:14 -0400 Subject: [PATCH 02/17] lib/markdown2: introduce markdown AST inline parser Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_inline_parsing.nit | 1401 +++++++++++++++++++++ 1 file changed, 1401 insertions(+) create mode 100644 lib/markdown2/markdown_inline_parsing.nit diff --git a/lib/markdown2/markdown_inline_parsing.nit b/lib/markdown2/markdown_inline_parsing.nit new file mode 100644 index 0000000000..703f46ef90 --- /dev/null +++ b/lib/markdown2/markdown_inline_parsing.nit @@ -0,0 +1,1401 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Parser for inline markdown +# +# Used to create the AST representation of inline nodes like emphasis, code, links +# images etc. +module markdown_inline_parsing + +import markdown_ast + +# Parser for inline content (text, links, emphasis, etc) +class MdInlineParser + + # List of delimiter processors to use + private var delimiter_processors: Array[MdDelimiterProcessor] is lazy do + var delimiters = new Array[MdDelimiterProcessor] + delimiters.add new MdAsteriskDelimiterProcessor + delimiters.add new MdUnderscoreDelimiterProcessor + return delimiters + end + + # Map special characters to their delimiter processor + private var delimiter_processors_map: Map[Char, MdDelimiterProcessor] is lazy do + var map = new HashMap[Char, MdDelimiterProcessor] + for delimiter_processor in delimiter_processors do + add_delimiter_processor(delimiter_processor, map) + end + special_characters.add_all map.keys + return map + end + + # Register a delimiter processor + private fun add_delimiter_processor(delimiter_processor: MdDelimiterProcessor, map: Map[Char, MdDelimiterProcessor]) do + var opening = delimiter_processor.opening_delimiter + var closing = delimiter_processor.closing_delimiter + if opening == closing then + if map.has_key(opening) then + var old = map[opening] + if old.opening_delimiter == old.closing_delimiter then + var s: MdStaggeredDelimiterProcessor + if old isa MdStaggeredDelimiterProcessor then + s = old + else + s = new MdStaggeredDelimiterProcessor(opening) + s.add old + end + s.add delimiter_processor + map[opening] = s + else + add_delimiter_processor_for_char(opening, delimiter_processor, map) + end + else + add_delimiter_processor_for_char(opening, delimiter_processor, map) + end + else + add_delimiter_processor_for_char(opening, delimiter_processor, map) + add_delimiter_processor_for_char(closing, delimiter_processor, map) + end + end + + # Register a delimiter processor for a special character + private fun add_delimiter_processor_for_char(delimiter_char: Char, delimiter_processor: MdDelimiterProcessor, map: Map[Char, MdDelimiterProcessor]) do + assert not map.has_key(delimiter_char) else + print "Delimiter processor conflict with delimiter char `{delimiter_char}`" + end + map[delimiter_char] = delimiter_processor + end + + # List of characters that have a special Markdown meaning + private var special_characters: Array[Char] = ['\n', '`', '[', ']', '\\', '!', '<', '&'] + + # Link references by ID, needs to be built up using `parse_reference` before calling `parse` + private var reference_map = new HashMap[String, MdLink] + + # Current block under parsing + private var block: MdNode is noinit + + # Current input string + private var input: String is noinit + + # Current index + private var index: Int is noinit + + # Current line + private var line: Int is noinit + + # Current column + private var column: Int is noinit + + # Current column offset + private var column_offset: Int is noinit + + # Top delimiter (emphasis, strong emphasis or custom emphasis) + # Brackets are on a separate stack, different from the algorithm described in the spec. + private var last_delimiter: nullable MdDelimiter = null + + # Top opening bracket (`[` or `![`) + private var last_bracket: nullable MdBracket = null + + # Parse `input` as inline and add resulting nodes as children to `block` + fun parse(input: String, offset: Int, block: MdNode) do + self.block = block + self.input = input.trim + self.index = 0 + self.last_delimiter = null + self.last_bracket = null + self.line = block.location.line_start + self.column_offset = offset + self.column = 1 + column_offset + + var more_to_parse = parse_inline + while more_to_parse do + more_to_parse = parse_inline + end + + process_delimiters(null) + merge_child_text_nodes(block) + end + + # Advance the current index of `count` characters + private fun advance(count: Int) do + index += count + column += count + end + + # Attempt to parse a link reference + # + # Return how many characters were parsed as a reference. + # Returns 0 if none. + fun parse_reference(input: String): Int do + self.input = input + self.index = 0 + self.column = 0 + var dest + var title + var match_chars + var start_index = index + + # label + match_chars = parse_link_label + if match_chars == 0 then return 0 + advance match_chars + + var raw_label = input.substring(0, match_chars) + + # colon + if peek != ':' then return 0 + advance 1 + + # link url + spnl + + dest = parse_link_destination.first + if dest == null or dest.is_empty then return 0 + + var before_title = index + var before_column = column + spnl + title = parse_link_title + if title == null then + # rewind before spaces + index = before_title + column = before_column + end + + var at_line_end = true + if index != input.length and match(re_line_end) == null then + if title == null then + at_line_end = false + else + # the potential title we found is not at the line end, + # but it could still be a legal link reference if we discard the title + title = null + # rewind before spaces + index = before_title + column = before_column + # and instead check if the link URL is at the line end + at_line_end = match(re_line_end) != null + end + end + + if not at_line_end then return 0 + + var normalized_label = raw_label.normalize_reference + if normalized_label.is_empty then return 0 + + if not reference_map.has_key(normalized_label) then + var link = new MdLink(new MdLocation(0, 0, 0, 0), dest, title) + reference_map[normalized_label] = link + end + + return index - start_index + end + + # Line end pattern + private var re_line_end: Regex = "^ *(\n|$)".to_re + + # Append standard text to the current block + # + # Read `text` between `begin_index` and `end_index`. + private fun append_text(text: String, begin_index, end_index: nullable Int): MdText do + var node: MdText + if begin_index != null and end_index != null then + var nb_chars = end_index - begin_index + var string = text.substring(begin_index, nb_chars) + node = new MdText( + new MdLocation( + line, + column, + line, + column + nb_chars - 1 + ), string) + else + node = new MdText( + new MdLocation( + line, + column, + line, + column + text.length + ), text) + end + append_node(node) + return node + end + + # Append `node` to the current block + private fun append_node(node: MdNode) do block.append_child(node) + + # Parse the next inline element in subject, advancing input index + # + # On success, add the result to block's children and return true. + # On failure, return false. + private fun parse_inline: Bool do + var res: Bool + var c = peek + if c == '\0' then return false + if c == '\n' then + res = parse_newline + else if c == '\\' then + res = parse_backslash + else if c == '`' then + res = parse_backticks + else if c == '[' then + res = parse_open_bracket + else if c == '!' then + res = parse_bang + else if c == ']' then + res = parse_close_bracket + else if c == '<' then + res = parse_auto_link or parse_html_inline + else if c == '&' then + res = parse_entity + else + if delimiter_processors_map.has_key(c) then + res = parse_delimiters(delimiter_processors_map[c], c) + else + res = parse_string + end + end + + if not res then + advance 1 + # When we get here, it's only for a single special character that turned + # out to not have a special meaning. + # So we shouldn't have a single surrogate here, hence it should be ok + # to turn it into a String + var literal = c.to_s + append_text(literal) + end + + return true + end + + # If `re` matches at current index in the input, advance index and return the match + # Else return null. + private fun match(re: Pattern): nullable String do + if index >= input.length then return null + var match = input.search_from(re, index) + if match != null then + index = match.after + column = match.after + return match.to_s + end + return null + end + + # Return the char at the current input index, or `\0` + private fun peek: Char do + if index < input.length then + return input.chars[index] + end + return '\0' + end + + # Return the char at the current input index + 1, or `\0` + private fun peek_next: Char do + if index + 1 < input.length then + return input.chars[index + 1] + end + return '\0' + end + + # Parse zero or more space characters, incuding at most one newline + private fun spnl: Bool do + var found_nl = false + loop + var c = peek + if c == ' ' or c == '\t' then + advance 1 + continue + else if c == '\n' then + if found_nl then break + found_nl = true + advance 1 + continue + end + break + end + return true + end + + # Parse a new line + # + # If it was preceded by two spaces, return a hard line break, + # otherwise a soft line break + private fun parse_newline: Bool do + advance 1 # assume we're at a `\n` + + var last_child = block.last_child + + # check previous text for trailing spaces + # the `has_suffix` is an optimization to avoid an RE match in the common case + if last_child != null and last_child isa MdText and + (last_child.literal.has_suffix(" ")) then + var text = last_child + var literal = text.literal + var match = literal.search(re_final_space) + var spaces = if match != null then match.length else 0 + if spaces > 0 then + text.literal = literal.substring(0, literal.length - spaces) + end + last_child.location.column_end = last_child.location.column_end - spaces + if spaces >= 2 then + append_node(new MdHardLineBreak(new MdLocation(line, column - spaces - 1, line, column - 1), false)) + else + append_node(new MdSoftLineBreak(new MdLocation(line, column - spaces - 1, line, column -1))) + end + else + append_node(new MdSoftLineBreak(new MdLocation(line, column - 1, line, column - 1))) + end + line += 1 + column = 1 + column_offset + + # gobble leading spaces in next line + while peek == ' ' do + advance 1 + end + return true + end + + # Final white spaces pattern + private var re_final_space: Regex = " *$".to_re + + # Parse a backslash-escaped special character + # + # Add either the escaped characters, a hard line break (if the backslash is followed by + # a new line), or a literal backslash to the block's children. + private fun parse_backslash: Bool do + advance 1 + if peek == '\n' then + append_node(new MdHardLineBreak(new MdLocation(line, column - 1, line, column), true)) + advance 1 + line += 1 + column = 1 + column_offset + else if index < input.length and input.substring(index, 1).has(re_escapable) then + append_text(input, index, index + 1) + advance 1 + else + append_text("\\") + end + return true + end + + # Escapable characters pattern + private var p_escapable = "[]!\"#$%&\'()*+,./:;<=>?@\\[\\\\^_`\\\{|\\\}~-]" + + # Escapable characters regex + private var re_escapable: Regex = "^{p_escapable}".to_re + + # Attempt to parse backticks + # + # Adding either a backtick code span or a literal sequence of backticks. + private fun parse_backticks: Bool do + var column_before = column + var ticks = match(re_ticks_here) + if ticks == null then return false + + var after_open_ticks = index + var matched = match(re_ticks) + while matched != null do + if matched == ticks then + var content = input.substring(after_open_ticks, index - after_open_ticks - ticks.length) + content = content.trim + content = content.replace(re_whitespace, " ") + var node = new MdCode(new MdLocation(line, column_before, line, column), matched.to_s, content.trim) + append_node(node) + column += 1 + return true + end + matched = match(re_ticks) + end + # If we got here, we didn't match a closing backtick sequence + index = after_open_ticks + column = after_open_ticks + 1 + append_text(ticks) + return true + end + + # Backticks starting pattern + private var re_ticks_here: Regex = "^`+".to_re + + # Backticks pattern + private var re_ticks: Regex = "`+".to_re + + # Attempt to parse delimiters like emphasis, strong emphasis or custom delimiters + private fun parse_delimiters(delimiter_processor: MdDelimiterProcessor, delimiter_char: Char): Bool do + var res = scan_delimiters(delimiter_processor, delimiter_char) + if res == null then return false + + var length = res.count + var start_index = index + var start_column = column + + advance length + var column_before = column + column = start_column + var node = append_text(input, start_index, index) + column = column_before + + # Add entry to stack for this opener + var last_delimiter = new MdDelimiter(node, delimiter_char, res.can_open, res.can_close, last_delimiter) + last_delimiter.length = length + last_delimiter.original_length = length + + var prev = last_delimiter.prev + if prev != null then + prev.next = last_delimiter + end + self.last_delimiter = last_delimiter + return true + end + + # Add open bracket to delimiter stack and add a text node to block's children + private fun parse_open_bracket: Bool do + var start_index = index + advance 1 + + var node = append_text("[") + + # Add entry to stack for this opener + add_bracket(new MdBracket.link(node, start_index, column - 1, last_bracket, last_delimiter)) + return true + end + + # If next character is `[`, add `!` delimiter to delimiter stack and add a text node to + # block's children. + # Otherwise just add a text node. + private fun parse_bang: Bool do + var start_index = index + advance 1 + + if peek == '[' then + advance 1 + var node = append_text("![") + + # Add entry to stack for this opener + add_bracket(new MdBracket.image(node, start_index + 1, column - 2, last_bracket, last_delimiter)) + else + append_text("!") + end + return true + end + + # Try match close bracket against an opening delimiter stack + # + # Add either a link or image, or a plan `[` character, to block's children. + # If there is a matching delimiter, remove it from the delimiter stack. + private fun parse_close_bracket: Bool do + advance 1 + var start_index = index + var start_column = column + + # Get previous `[` or `![` + var opener = last_bracket + if opener == null then + # no matching opener, just return a literal + append_text("]") + return true + end + + if not opener.allowed then + # matching opener but it's not allowed, juste return a literal + append_text("]") + remove_last_bracket + return true + end + + # check to see if we have a link or image + var dest: nullable Couple[nullable String, Bool] = null + var title = null + var is_link_or_image = false + + # maybe an inline link like `[foo](\uri "title")` + if peek == '(' then + advance 1 + spnl + dest = parse_link_destination + if dest.first != null then + spnl + # title needs a whitespace before + if input.substring(index - 1, 1).has(re_whitespace) then + title = parse_link_title + spnl + end + if peek == ')' then + advance 1 + is_link_or_image = true + else + index = start_index + column = start_column + end + end + end + + # maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]` + if not is_link_or_image then + # see if there's a link label like `[bar]` or `[]` + var before_label = index + var label_length = parse_link_label + advance label_length + var ref = null + if label_length > 2 then + ref = input.substring(before_label, label_length) + else if not opener.bracket_after then + # If the second label is empty `[foo][]` or missing `[foo]`, then the first label + # is the reference. + # But it can only be a reference when there's no (unescaped) bracket in it. + # If there is, we don't even need to try to lookup the reference. + ref = input.substring(opener.index, start_index - opener.index) + end + + if ref != null then + var nref = ref.normalize_reference + if reference_map.has_key(nref) then + var link = reference_map[nref] + dest = new Couple[nullable String, Bool](link.destination, false) + title = link.title + is_link_or_image = true + end + end + end + + if is_link_or_image then + # If we got here, open is a potential opener + var link_or_image: MdLinkOrImage + if opener.is_image then + link_or_image = new MdImage(new MdLocation(line, opener.column, line, column - 1), dest.as(not null).first or else "", title) + else + link_or_image = new MdLink(new MdLocation(line, opener.column, line, column - 1), dest.as(not null).first or else "", title) + end + link_or_image.has_brackets = dest.as(not null).second + + var node = opener.node.next + while node != null do + var next = node.next + link_or_image.append_child(node) + node = next + end + append_node(link_or_image) + + # Process delimiters such as emphasis inside a link/image + process_delimiters(opener.prev_delimiter) + merge_child_text_nodes(link_or_image) + # We don't need the corresponding text node anymore, we turned it into a node + opener.node.unlink + remove_last_bracket + + # Links within links are not allowed + # We found this link, so there can be no other link around it. + if not opener.is_image then + var bracket = last_bracket + while bracket != null do + if not bracket.is_image then + # disallow link opener + bracket.allowed = false + end + bracket = bracket.prev + end + end + return true + end + + if not is_link_or_image then + if parse_wikilink then return true + end + + # no link or image + append_text("]") + remove_last_bracket + index = start_index + column = start_column + return true + end + + # Whitespace pattern + private var re_whitespace: Regex = "\\s+".to_re + + # Add a bracket token on top of the `last_bracket` stack + private fun add_bracket(bracket: MdBracket) do + var last_bracket = self.last_bracket + if last_bracket != null then + last_bracket.bracket_after = true + end + self.last_bracket = bracket + end + + # Remove the last bracket on the `last_bracket` stack + private fun remove_last_bracket do + var last_bracket = self.last_bracket + if last_bracket == null then return + self.last_bracket = last_bracket.prev + end + + # Wikilink placeholder + # + # Will be defined in sub module. + private fun parse_wikilink: Bool do return false + + # Attempt to parse a link destination, returning the string or null if not match + private fun parse_link_destination: Couple[nullable String, Bool] do + var buffer = new Buffer + + var c = peek + var parens = 0 + + var has_bracket = c == '<' + if has_bracket then advance 1 + + loop + c = peek + if c == '\0' then + break # end of input + else if c == ' ' or c == '\t' or c == '\n' or c == '\r' then + break # no spaces allowed in urls + else if c == '\\' then + var next = peek_next + if escapable.has(next) then + buffer.add next + advance 2 # skip over the backslash + continue + end + else if has_bracket and c == '>' then + advance 1 + break + else if not has_bracket and c == '(' then + parens += 1 + else if not has_bracket and c == ')' then + if parens == 0 then break + parens -= 1 + else if c == '\0' then + break + end + buffer.add c + advance 1 + end + return new Couple[nullable String, Bool](buffer.to_s, has_bracket) + end + + # Attempt to parse a link title (sans quotes), returning the string or null if no match + private fun parse_link_title: nullable String do + var c = peek + if c != '\'' and c != '"' and c != '(' then + return null + end + var opener = c + + var buffer = new Buffer + loop + advance 1 + c = peek + if c == opener or (opener == '(' and c == ')') then + advance 1 + break + else if c == '\\' then + var next = peek_next + if escapable.has(next) then + buffer.add next + advance 1 + continue + end + else if c == '\0' then + return null + end + buffer.add c + end + return buffer.to_s + end + + # Escapable characters + private var escapable = "[]!\"#$%&\'()*+,./:;<=>?@\\^_`\{|\}~-" + + # Attempt to parse a link label returning number of characters parsed + private fun parse_link_label: Int do + var i = index + while i < input.length do + var c = input[i] + if i == index and c != '[' then + return 0 + else if c == '[' and i != index then + if input[i - 1] != '\\' or (i - 2 > index and input[i - 2] == '\\') then + return 0 + end + else if c == ']' then + if i > 1001 then return 0 + if input[i - 1] != '\\' or (i - 2 > index and input[i - 2] == '\\') then + return (i - index) + 1 + end + end + i += 1 + end + return 0 + end + + # Attempt to parse an autolink (URL or email in pointy brackets) + private fun parse_auto_link: Bool do + var column_before = column + var m = match(re_autolink_email) + if m != null then + var dest = m.substring(1, m.length - 2) + var node = new MdLink(new MdLocation(line, column_before, line, column), "mailto:{dest}", null, true) + node.append_child(new MdText(new MdLocation(line, column_before + 1, line, column - 1), dest)) + column += 1 + append_node(node) + return true + end + m = match(re_autolink_url) + if m != null then + var dest = m.substring(1, m.length - 2) + var node = new MdLink(new MdLocation(line, column_before, line, column), dest, null, true) + node.append_child(new MdText(new MdLocation(line, column_before + 1, line, column - 1), dest)) + column += 1 + append_node(node) + return true + end + return false + end + + # Autolink email pattern + private var re_autolink_email: Regex = "^<([a-zA-Z0-9.!#$%&'*+/=?^_`\{|\}~-]+@[a-zA-Z0-9]([a-zA-Z0-9-]\{0,61\}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]\{0,61\}[a-zA-Z0-9])?)*)>".to_re + + # Autolink url pattern + private var re_autolink_url: Regex = "^<[a-zA-Z][a-zA-Z0-9.+-]\{1,31\}:[^<> ]*>".to_re + + # Attempt to parse an inline HTML string + private fun parse_html_inline: Bool do + var column_before = column + var m = match(re_html_tag) + if m != null then + var node = new MdHtmlInline(new MdLocation(line, column_before, line, column), m) + column += 1 + append_node(node) + return true + end + return false + end + + private var p_tagname = "[A-Za-z][A-Za-z0-9-]*" + private var p_attribute_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*" + private var p_uquoted_value = "[^\"'=<>` \t\n]+" + private var p_squoted_value = "'[^']*'" + private var p_dquoted_value = "\"[^\"]*\"" + private var p_attribute_value = "({p_uquoted_value}|{p_squoted_value}|{p_dquoted_value})" + private var p_attribute_value_spec = "(\\s*=\\s*{p_attribute_value})" + private var p_attribute = "(\\s{p_attribute_name}{p_attribute_value_spec}?)" + private var p_opentag = "<{p_tagname}{p_attribute}*\\s*/?>" + private var p_closetag = "]" + private var p_html_comment = "|" + private var p_processing_instruction = "[<][?].*?[?][>]" + private var p_declaration = "]*>" + private var p_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 From fffcf9bbebc894acccfd575d781bd6985b344e47 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:51:24 -0400 Subject: [PATCH 03/17] lib/markdown2: introduce markdown block parser Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_block_parsing.nit | 1503 ++++++++++++++++++++++ 1 file changed, 1503 insertions(+) create mode 100644 lib/markdown2/markdown_block_parsing.nit diff --git a/lib/markdown2/markdown_block_parsing.nit b/lib/markdown2/markdown_block_parsing.nit new file mode 100644 index 0000000000..cfa07b56d6 --- /dev/null +++ b/lib/markdown2/markdown_block_parsing.nit @@ -0,0 +1,1503 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown blocks parsing +# +# Introduce the parsers for the different Markdown blocks such as headings, lists +# code blocks etc. +module markdown_block_parsing + +import markdown_inline_parsing + +# Markdown parser +# +# Used to create the AST representation of a Markdown document. +class MdParser + + # Inline parser used to parse block content + private var inline_parser = new MdInlineParser is lazy + + # Block parsers factories + private var block_parser_factories: Collection[MdBlockParserFactory] do + var factories = new Array[MdBlockParserFactory] + factories.add new MdBlockQuoteParserFactory + factories.add new MdHeadingParserFactory + factories.add new MdFencedCodeBlockParserFactory + factories.add new MdHtmlBlockParserFactory + factories.add new MdThematicBreakParserFactory + factories.add new MdListBlockParserFactory + factories.add new MdIndentedCodeBlockParserFactory + return factories + end + + # Active block parsers + # + # Used as a stack to parse nested blocks. + private var active_block_parsers = new Array[MdBlockParser] + + # All active block parsers + private var all_block_parsers = new HashSet[MdBlockParser] + + # Return the active block parser + # + # The last entry in the `active_block_parsers` stack. + private fun active_block_parser: MdBlockParser do + return active_block_parsers.last + end + + # Activate a `block_parser` + # + # Add the `block_parser` on the top of the `active_block_parsers` stack. + # Also register it in `all_block_parsers`. + private fun activate_block_parser(block_parser: MdBlockParser) do + active_block_parsers.add block_parser + all_block_parsers.add block_parser + end + + # Deactivate the `active_block_parser` + private fun deactivate_block_parser do + active_block_parsers.pop + end + + # Deactivate and remove the `active_block_parser` from the `all_block_parsers` list + private fun remove_active_block_parser do + var old = active_block_parser + deactivate_block_parser + all_block_parsers.remove(old) + old.block.unlink + end + + # Post-processors applied after the parsing of a document + var post_processors = new Array[MdPostProcessor] is writable + + # Currently parsed line + private var line_string: String is noinit + + # Current index (offset) in input `line_string` (starts at 0) + private var index = 0 + + # Current column in input `line_string` (starts at 0) + # + # Tab causes column to go to next 4-space tab stop. + private var column = 0 + + # Is the current column within a tab character (partially consumed tab) + private var column_is_in_tab: Bool is noinit + + # Current line in input string (starts at 1) + private var line = 1 + + # Index of the next non-space character starting from `index` + private var next_non_space_index = 0 + + # Next non-space column + private var next_non_space_column = 0 + + # Current indent in columns + # + # Either by spaces or tab stop of 4, starting from `column`. + private var indent = 0 + + # Is the current `line` blank starting from `index`? + private var is_blank: Bool is noinit + + # Does a node end with a blank line? + private var last_line_blank = new HashMap[MdNode, Bool] + + # Initialize parser state + private fun initialize do + active_block_parsers.clear + all_block_parsers.clear + index = 0 + column = 0 + column_is_in_tab = false + line = 1 + next_non_space_index = 0 + next_non_space_column = 0 + indent = 0 + is_blank = false + last_line_blank.clear + end + + # Parse the `input` string as a MdDocument + fun parse(input: String): MdDocument do + initialize + + var document_block_parser = new MdDocumentBlockParser(1, 1, 0) + activate_block_parser(document_block_parser) + var line_start = 0 + var line_break = find_line_break(input, line_start) + while line_break != -1 do + var line_string = input.substring(line_start, line_break - line_start) + incorporate_line(line_string) + if line_break + 1 < input.length and + input.chars[line_break] == '\r' and + input.chars[line_break + 1] == '\n' then + line_start = line_break + 2 + else + line_start = line_break + 1 + end + line_break = find_line_break(input, line_start) + line += 1 + column = 0 + end + + # Finalize pending line + if input.length > 0 and (line_start == 0 or line_start < input.length) then + incorporate_line(input.substring(line_start, input.length - line_start)) + end + finalize_blocks(active_block_parsers) + + # Walk through a block and its chiildren revursively + # Parsing string content into inline content where appropriate. + var all_block_parsers = all_block_parsers.to_a + var i = all_block_parsers.length - 1 + while i >= 0 do + var block_parser = all_block_parsers[i] + block_parser.parse_inlines(inline_parser) + i -= 1 + end + var document = document_block_parser.block + return document + end + + # Post-process the `document` + fun post_process(document: MdDocument) do + for processor in post_processors do + processor.post_process(self, document) + end + end + + # Analyze a line of text and update the document + # + # We parse Markdown text by calling this on each line of `input`. + private fun incorporate_line(input: String) do + line_string = input + index = 0 + column = 0 + column_is_in_tab = false + + # For each containing block, try to parse the associated line start. + var matches = 1 + for i in [1 .. active_block_parsers.length[ do + var block_parser = active_block_parsers[i] + find_next_non_space + + var result = block_parser.try_continue(self) + if result isa MdBlockContinue then + if result.is_finalize then + block_parser.finalize(self) + return + else + if result.new_index != -1 then + set_new_index result.new_index + else if result.new_column != -1 then + set_new_column result.new_column + end + end + matches += 1 + else + break + end + end + + var unmatched_block_parsers = active_block_parsers.subarray( + matches, active_block_parsers.length - matches) + var last_matched_block_parser = active_block_parsers[matches - 1] + var block_parser = last_matched_block_parser + var all_closed = unmatched_block_parsers.is_empty + + # Unless last matched container is a code block, try new container starts, + # adding children to the last matched container. + var try_block_starts = block_parser.block isa MdParagraph or + block_parser.block.is_container + + while try_block_starts do + find_next_non_space + + # Optimize lookup + if is_blank or (indent < 4 and line_string.chars[next_non_space_index].is_letter) then + set_new_index next_non_space_index + break + end + + var block_start = find_block_start(block_parser) + if block_start == null then + set_new_index next_non_space_index + break + end + + if not all_closed then + finalize_blocks(unmatched_block_parsers) + all_closed = true + end + + if block_start.new_index != -1 then + set_new_index block_start.new_index + else if block_start.new_column != -1 then + set_new_column block_start.new_column + end + + if block_start.replace_active_block_parser then + remove_active_block_parser + end + + for new_block_parser in block_start.block_parsers do + add_child(new_block_parser) + block_parser = new_block_parser + try_block_starts = new_block_parser.block.is_container + end + end + + # What remains at the offset is a text line. + # Add the text to the appropriate block. + + # First check for a lazy paragraph continuation + if not all_closed and not is_blank and active_block_parser isa MdParagraphParser then + add_line + else + # Finalize any blocks not matched + if not all_closed then + finalize_blocks(unmatched_block_parsers) + end + propagate_last_line_blank(block_parser, last_matched_block_parser) + + if not block_parser.block.is_container then + add_line + else if not is_blank then + # Create a paragraph container for the line + add_child(new MdParagraphParser(line, column + 1, block_parser.content_offset)) + add_line + end + end + end + + # Find what kind of block starts at `index` in `input` + private fun find_block_start(block_parser: MdBlockParser): nullable MdBlockStart do + for block_parser_factory in block_parser_factories do + var result = block_parser_factory.try_start(self, block_parser) + if result != null then return result + end + return null + end + + # Add a `block_parser` block's as child of the active block parser block + private fun add_child(block_parser: MdBlockParser) do + # Finalize non-parentable blocks + while not active_block_parser.block.can_contain(block_parser.block) do + active_block_parser.finalize(self) + end + # Append block block parser block to its parent + active_block_parser.block.append_child(block_parser.block) + activate_block_parser(block_parser) + end + + # Add line content to the active block parser + # + # We assume it can accept lines. + private fun add_line do + var content = null + if column_is_in_tab then + # Out column is in a partially consumed tab. + # Expand the remaining columns to the next tab stop to spaces. + var after_tab = index + 1 + var rest = line_string.substring(after_tab, line_string.length - after_tab) + var spaces = column.columns_to_next_tab_stop + var buffer = new Buffer + for i in [0 .. spaces[ do + buffer.add ' ' + end + buffer.append(rest) + content = buffer.write_to_string + else + content = line_string.substring(index, line_string.length - index) + end + active_block_parser.add_line(content) + end + + # Finalize blocks of previous line + private fun finalize_blocks(block_parsers: Sequence[MdBlockParser]) do + var i = block_parsers.length - 1 + while i >= 0 do + var block_parser = block_parsers[i] + block_parser.finalize(self) + i -= 1 + end + end + + # Advance the `index` position to the next character + # + # Also set the `column`. + # If the next character is a tab, compute the new column accordingly. + private fun advance do + var c = line_string.chars[index] + if c == '\t' then + index += 1 + column += column.columns_to_next_tab_stop + else + index += 1 + column += 1 + end + end + + # Move `index` to the next non-space character index in the `input` string + # + # Also set `next_non_space_index`, `next_non_space_column`, `is_blank` and `indent`. + private fun find_next_non_space do + var i = index + var cols = column + + is_blank = true + while i < line_string.length do + var c = line_string.chars[i] + if c == ' ' then + i += 1 + cols += 1 + continue + else if c == '\t' then + i += 1 + cols += 4 - (cols % 4) + continue + end + is_blank = false + break + end + + next_non_space_index = i + next_non_space_column = cols + indent = next_non_space_column - column + end + + # Return the position of the next line break + # + # We consider `\r` and `\n`. + private fun find_line_break(input: String, start_index: Int): Int do + for i in [start_index .. input.length[ do + var char = input.chars[i] + if char == '\r' or char == '\n' then return i + end + return -1 + end + + # Set the parser `index` at `new_index` + # + # Also set `column` and `column_is_in_tab`. + private fun set_new_index(new_index: Int) do + if new_index >= next_non_space_index then + # We can start from here, no need to calculate tab stops again + index = next_non_space_index + column = next_non_space_column + end + while index < new_index and index != line_string.length do + advance + end + # If we're going to an index as opposed to a column, we're never within a tab + column_is_in_tab = false + end + + # Set the parser `column` at `new_column` + # + # Also set `index` and `column_is_in_tab`. + private fun set_new_column(new_column: Int) do + if new_column >= next_non_space_column then + # We can start from here, no need to calculate tab stops again + index = next_non_space_index + column = next_non_space_column + end + while column < new_column and index != line_string.length do + advance + end + if column > new_column then + # Last character was a tab and we overshot our target + index -= 1 + column = new_column + column_is_in_tab = true + else + column_is_in_tab = false + end + end + + # Does `block` end with a blank line? + private fun ends_with_blank_line(block: nullable MdNode): Bool do + while block != null do + if is_last_line_blank(block) then return true + if block isa MdListBlock or block isa MdListItem then + block = block.last_child + else + break + end + end + return false + end + + # Propagate a blank line to all block_parser blocl's parents + private fun propagate_last_line_blank(block_parser: MdBlockParser, last_matched_block_parser: MdBlockParser) do + var last_child = block_parser.block.last_child + if is_blank and last_child != null then + last_line_blank[last_child] = true + end + var block = block_parser.block + + # Block quotes lines are never blank as they start with `>`. + # We don't count blanks in fenced code for purposes of thight/loose lists. + # We also don't set `last_line_blank` on an empty list item. + var last_line_blank = is_blank and + not (block isa MdBlockQuote or + block isa MdFencedCodeBlock or + (block isa MdListItem and block.first_child == null and + block_parser != last_matched_block_parser)) + + # Propagate `last_line_blank` up through parents + var node: nullable MdNode = block_parser.block + while node != null do + self.last_line_blank[node] = last_line_blank + node = node.parent + end + end + + # Is last line blank for `node`? + private fun is_last_line_blank(node: MdNode): Bool do + if not last_line_blank.has_key(node) then return false + return last_line_blank[node] + end +end + +# Block parsing + +# Parser for a specific block node +abstract class MdBlockParser + + # Kind of block under construction + type BLOCK: MdBlock + + # MdBlock under construction + fun block: BLOCK is abstract + + # Line Start + var line_start: Int + + # Column start + var column_start: Int + + # Location at start + # + # The location end it initialized at `-1` and will be set later in the + # `finalize` method. + var location: MdLocation is lazy do return new MdLocation(line_start, column_start, -1, -1) + + # Column where the content starts + var content_offset: Int + + # Initialize the current `block` + fun initialize(parser: MdParser) do end + + # Can `self` continue from the current `index` in `parser`? + # + # Return a new `MdBlockContinue` if `self` can continue parsing. + # Return null otherwise. + fun try_continue(state: MdParser): nullable MdBlockContinue is abstract + + # Add `line` to the current `block` + fun add_line(line: String) do end + + # Finalize the current `block` + # + # Deactivate `self` from `parser` and call `close_block`. + fun finalize(parser: MdParser) do + if parser.active_block_parser == self then + parser.deactivate_block_parser + end + end + + # Parse `block` lines + fun parse_inlines(inline_parser: MdInlineParser) do end +end + +# Result object for continuing parsing of a block +class MdBlockContinue + + # Index from which continue parsing + var new_index: Int + + # Column from which continue parsing + var new_column: Int + + # Is the block finalized? + var is_finalize: Bool + + # Continue from index + init at_index(new_index: Int) do + init(new_index, -1, false) + end + + # Continue from column + init at_column(new_column: Int) do + init(-1, new_column, false) + end + + # Block is finished + init finished do + init(-1, -1, true) + end +end + +# Block parser factory for a block node for determining when a block starts +abstract class MdBlockParserFactory + + # Can the associated block parser can start at the current line in `parser`? + # + # Return a new `MdBlockStart` if the block parser can start. + # Return null otherwise. + fun try_start(parser: MdParser, matched_block_parser: MdBlockParser): + nullable MdBlockStart is abstract +end + +# Result object from starting parsing of a block +class MdBlockStart + + # Block parsers for this block start + var block_parsers: Array[MdBlockParser] + + # Index where the parsing should start + var new_index = -1 + + # Column where the parsing should start + var new_column = -1 + + # Does the block starting with `self` terminate a previous block? + var replace_active_block_parser = false + + # Start from `new_index` + fun at_index(new_index: Int): MdBlockStart do + self.new_index = new_index + return self + end + + # Start from `new_column` + fun at_column(new_column: Int): MdBlockStart do + self.new_column = new_column + return self + end + + # Start replacing the active block parser + fun replacing_active_block_parser: MdBlockStart do + self.replace_active_block_parser = true + return self + end +end + +# Parser for the whole document +class MdDocumentBlockParser + super MdBlockParser + + redef type BLOCK: MdDocument + redef var block = new MdDocument(location) is lazy + + # Always continue at current indent + redef fun try_continue(state) do return new MdBlockContinue.at_index(state.index) + + redef fun finalize(parser) do + end + + # redef fun finalize(state) do + redef fun parse_inlines(inline_parser) do + var last_child = block.last_child + if last_child != null then + location.line_end = last_child.location.line_end + location.column_end = last_child.location.column_end + end + end +end + +# Headings parser +class MdHeadingParser + super MdBlockParser + + redef type BLOCK: MdHeading + + redef var block = new MdHeading(location, level, is_setext, has_atx_trailing) is lazy + + redef var location = new MdLocation(line_start, column_start, line_end, column_end) is lazy + + # Line end + var line_end: Int + + # Column end + var column_end: Int + + # Heading level + var level: Int + + # Heading content + var content: String + + # Heading has ATX trailing + var has_atx_trailing: Bool + + # Heading is setext format + var is_setext: Bool + + # Never continue parsing as an heading is a one liner + redef fun try_continue(state) do return null + + # Parse the heading content + redef fun parse_inlines(inline_parser) do + inline_parser.parse(content, content_offset, block) + end +end + +# Heading parser factory +class MdHeadingParserFactory + super MdBlockParserFactory + + redef fun try_start(state, matched_block_parser) do + if state.indent >= 4 then return null + + var next_non_space = state.next_non_space_index + var line = state.line_string + var paragraph = null + if matched_block_parser isa MdParagraphParser then + paragraph = matched_block_parser.content + end + + var line_content = line.substring(next_non_space, line.length - next_non_space) + var match = line_content.search(re_atx_heading) + if match != null then + # ATX heading + var new_offset = next_non_space + match.subs.first.as(not null).length + var level = match.subs.first.as(not null).to_s.trim.length + # remove trailing ###s + var after_leading = line.substring(new_offset, line.length - new_offset) + var trailing = after_leading.search(re_atx_trailing) + var has_trailing = trailing != null + var trailing_length = if trailing != null then trailing.length else 0 + var content = after_leading.replace(re_atx_trailing, "") + return (new MdBlockStart( + [new MdHeadingParser( + state.line, + next_non_space + 1, + new_offset + 1, + state.line, + new_offset + content.length + trailing_length, + level, + content, + has_trailing, false)]) + ).at_index(line.length) + end + + if paragraph == null then return null + + match = line_content.search(re_setext_heading) + if match == null then return null + var level = 2 + if match.subs.first.as(not null).to_s.chars.first == '=' then level = 1 + var content = paragraph.to_s + return (new MdBlockStart( + [new MdHeadingParser( + state.line - 1, + next_non_space + 1, + 0, + state.line, + state.column + match.length, + level, + content, + false, true)]) + ).at_index(line.length).replacing_active_block_parser + end +end + +# Blockquotes parser +class MdBlockQuoteParser + super MdBlockParser + + redef type BLOCK: MdBlockQuote + redef var block = new MdBlockQuote(location) is lazy + + redef fun try_continue(state) do + var next_non_space = state.next_non_space_index + var indent = state.indent + var line = state.line_string + + if indent >= 4 then return null + if next_non_space >= line.length then return null + if line.chars[next_non_space] != '>' then return null + + var new_column = state.column + state.indent + 1 + # optional following space or tab + if state.line_string.is_space_or_tab(next_non_space + 1) then + new_column += 1 + end + return new MdBlockContinue.at_column(new_column) + end + + redef fun parse_inlines(inline_parser) do + var last_child = block.last_child + if last_child != null then + location.line_end = last_child.location.line_end + location.column_end = last_child.location.column_end + end + end +end + +# Blockquotes parser factory +class MdBlockQuoteParserFactory + super MdBlockParserFactory + + redef fun try_start(state, matched_block_parser) do + var next_non_space = state.next_non_space_index + var indent = state.indent + var line = state.line_string + + if indent >= 4 then return null + if next_non_space >= line.length then return null + if line.chars[next_non_space] != '>' then return null + + var new_column = state.column + state.indent + 1 + # optional following space or tab + if state.line_string.is_space_or_tab(next_non_space + 1) then + new_column += 1 + end + return (new MdBlockStart( + [new MdBlockQuoteParser( + state.line, + state.column + 1, + new_column)]) + ).at_column(new_column) + end +end + +# Indented code blocks parser +class MdIndentedCodeBlockParser + super MdBlockParser + + redef type BLOCK: MdIndentedCodeBlock + redef var block = new MdIndentedCodeBlock(location, use_tabs) is lazy + + # Indent is tab? + var use_tabs: Bool + + # Block content + var content = new Buffer + + redef fun try_continue(state) do + if state.indent >= 4 then + return new MdBlockContinue.at_column(state.column + 4) + else if state.is_blank then + return new MdBlockContinue.at_index(state.next_non_space_index) + end + return null + end + + redef fun add_line(line) do + if not content.is_empty then + content.add('\n') + end + content.append(line) + end + + redef fun finalize(parser) do + super + + add_line(" ") + var content = self.content.to_s + var literal = content.replace_first(re_trailing_blank_lines, "\n") + block.literal = literal + + var lines = literal.split("\n") + location.line_end = location.line_start + lines.length - 2 + location.column_end = content_offset + lines[lines.length - 2].length + 4 + end +end + +# Indented code blocks parser factory +class MdIndentedCodeBlockParserFactory + super MdBlockParserFactory + + redef fun try_start(state, matched_block_parser) do + if state.indent < 4 then return null + if state.is_blank then return null + if state.active_block_parser.block isa MdParagraph then return null + + var use_tabs = state.line_string.has_prefix("\t") + return (new MdBlockStart( + [new MdIndentedCodeBlockParser( + state.line, + state.column + 1, + state.column, + use_tabs)]) + ).at_column(state.column + 4) + end +end + +# Fenced code blocks parser +class MdFencedCodeBlockParser + super MdBlockParser + + redef type BLOCK: MdFencedCodeBlock + redef var block = new MdFencedCodeBlock(location, fence_char, fence_length, fence_indent) is lazy + + # Fence character + var fence_char: Char + + # Fence length + var fence_length: Int + + # Fence indent + var fence_indent: Int + + # Fence first line + var first_line: nullable String = null + + # Fence other lines + var other_lines = new Buffer + + redef fun try_continue(state) do + var next_non_space = state.next_non_space_index + var new_index = state.index + var line = state.line_string + + if state.indent <= 3 and next_non_space < line.length and + line.chars[next_non_space] == fence_char then + + var match = line.substring(next_non_space, line.length - next_non_space). + search(re_closing_fence) + if match != null and match.subs[0].as(not null).length >= fence_length then + # closing fence - we're at end of line, so we can finalize now + return new MdBlockContinue.finished + end + end + + # skip optional spaces of fence indent + var i = fence_indent + while i > 0 and new_index < line.length and line.chars[new_index] == ' ' do + new_index += 1 + i -= 1 + end + + return new MdBlockContinue.at_index(new_index) + end + + redef fun add_line(line) do + if first_line == null then + first_line = line + else + other_lines.append(line) + other_lines.add '\n' + end + end + + redef fun finalize(parser) do + super + + # first line become info string + var first_line = self.first_line + if first_line != null then + var info = first_line.trim.unescape_string + if not info.is_empty then block.info = info + end + + var content = other_lines.to_s + block.literal = content + + var lines = content.split("\n") + location.line_end = location.line_start + lines.length + location.column_end = content_offset + fence_indent + fence_length + end +end + +# Fenced code blocks parser factory +class MdFencedCodeBlockParserFactory + super MdBlockQuoteParserFactory + + redef fun try_start(state, matched_block_parser) do + var next_non_space = state.next_non_space_index + var line = state.line_string + + if state.indent >= 4 then return null + + var match = line.substring(next_non_space, line.length - next_non_space).search(re_opening_fence) + if match == null then return null + + var fence_length + var fence_char + var sub0 = match.subs[0] + if sub0 != null then + fence_length = sub0.length + fence_char = sub0.to_s.chars.first + else + fence_length = match.subs[2].as(not null).length + fence_char = match.subs[2].as(not null).to_s.chars.first + end + if fence_char == '`' and match.to_s.has("[^`]+`".to_re) then + return null + else if match.to_s.has("[^~]+~".to_re) then + return null + end + return (new MdBlockStart( + [new MdFencedCodeBlockParser( + state.line, + state.column + 1, + state.column, + fence_char, + fence_length, + state.indent)] + )).at_index(next_non_space + fence_length) + end +end + +# List blocks parser +class MdListBlockParser + super MdBlockParser + + redef type BLOCK: MdListBlock + + redef var block is lazy do + if is_ordered then + return new MdOrderedList(location, digit.as(not null), delim.as(not null)) + else + return new MdUnorderedList(location, bullet.as(not null)) + end + end + + # Is this list ordered + var is_ordered: Bool + + # List bullet if unordered + var bullet: nullable Char + + # List digit if ordered + var digit: nullable Int + + # List delimiter if ordered + var delim: nullable Char + + redef fun try_continue(state) do return new MdBlockContinue.at_index(state.index) + + redef fun finalize(parser) do + super + + var item = block.first_child + while item != null do + # check for non-final list item ending with blank line + if parser.ends_with_blank_line(item) and item.next != null then + block.is_tight = false + break + end + # recurse into children of list item to see if there are spaces between any of them + var sub_item = item.first_child + while sub_item != null do + if parser.ends_with_blank_line(sub_item) and + (item.next != null or sub_item.next != null) then + block.is_tight = false + break + end + sub_item = sub_item.next + end + item = item.next + end + end + + redef fun parse_inlines(inline_parser) do + var last_child = block.last_child + if last_child != null then + location.line_end = last_child.location.line_end + location.column_end = last_child.location.column_end + end + end +end + +# List blocks parser factory +class MdListBlockParserFactory + super MdBlockQuoteParserFactory + + redef fun try_start(state, matched_block_parser) do + if state.indent >= 4 and not matched_block_parser isa MdListBlockParser then return null + + var marker_index = state.next_non_space_index + var marker_column = state.column + state.indent + + var in_paragraph = matched_block_parser isa MdParagraphParser and matched_block_parser.content != null + var list_data = parse_list_marker(state, state.line_string, marker_index, marker_column, in_paragraph) + if list_data == null then return null + + + var new_column = list_data.content_column + var list_item_parser = new MdListItemParser( + state.line, + state.column + 1, + new_column, + new_column - state.column) + + # prepend the list block if needed + if not matched_block_parser isa MdListBlockParser or not lists_match(matched_block_parser.block, list_data) then + var list_block_parser = new MdListBlockParser(state.line, state.column + 1, new_column - state.column, list_data.is_ordered, list_data.bullet, list_data.digit, list_data.delim) + list_block_parser.block.is_tight = true + + return (new MdBlockStart([list_block_parser, list_item_parser: MdBlockParser])).at_column(new_column) + end + return (new MdBlockStart([list_item_parser])).at_column(new_column) + end + + private fun parse_list_marker(state: MdParser, line: String, marker_index, marker_column: Int, in_paragraph: Bool): nullable MdListData do + var rest = line.substring(marker_index, line.length - marker_index) + var match = rest.search(re_list_marker) + if match == null then return null + + var is_ordered + var bullet = null + var digit = null + var delim = null + + var bullet_match = match.subs[0] + if bullet_match != null then + is_ordered = false + bullet = bullet_match.to_s.chars[0] + else + is_ordered = true + digit = match.subs[2].as(not null).to_s.to_i + delim = match.subs[3].as(not null).to_s.chars[0] + end + + var marker_length = match.length + if match.to_s.has_suffix(" ") or match.to_s.has_suffix("\t") then + marker_length -= 1 + end + var index_after_marker = marker_index + marker_length + + # marker doesn't include tabs, so counting them as column directly is ok + var column_after_marker = marker_column + marker_length + # the column within the line where the content starts + var content_column = column_after_marker + + # see at which column the content starts if there is content + var has_content = false + for i in [index_after_marker .. line.length[ do + var c = line.chars[i] + if c == '\t' then + content_column += content_column.columns_to_next_tab_stop + else if c == ' ' then + content_column += 1 + else + has_content = true + break + end + end + + if in_paragraph then + # if the list item is ordered, then start number must be 1 to interrupt a paragraph + if is_ordered and digit != 1 then + return null + end + # empty list item can not interrupt a paragraph + if not has_content then + return null + end + end + + if not has_content or (content_column - column_after_marker) > 4 then + # if this line is blank or has a code block, default to 1 space after marker + content_column = column_after_marker + 1 + end + return new MdListData(is_ordered, bullet, digit, delim, content_column) + end + + # Return true if the two list items are of the same type + # + # With the same delimiter and bullet character. + # This is used in agglomerating list items into lists + private fun lists_match(a: MdListBlock, b: MdListData): Bool do + if a isa MdUnorderedList and not b.is_ordered then + return a.bullet_marker == b.bullet + else if a isa MdOrderedList and b.is_ordered then + return a.delimiter == b.delim + end + return false + end +end + +# Parsed list data +private class MdListData + + var is_ordered: Bool + + var bullet: nullable Char + + var digit: nullable Int + + var delim: nullable Char + + # Column the content start at + var content_column: Int +end + +# List items parser +class MdListItemParser + super MdBlockParser + + redef type BLOCK: MdListItem + redef var block = new MdListItem(location) is lazy + + # List item content indend + var content_indent: Int + + redef fun try_continue(state) do + if state.is_blank then + if block.first_child == null then + # blank line after empty list item + return null + end + return new MdBlockContinue.at_index(state.next_non_space_index) + end + if state.indent >= content_indent then + return new MdBlockContinue.at_column(state.column + content_indent) + end + return null + end + + redef fun parse_inlines(inline_parser) do + var last_child = block.last_child + if last_child != null then + location.line_end = last_child.location.line_end + location.column_end = last_child.location.column_end + end + end +end + +# Thematic breaks parser +class MdThematicBreakParser + super MdBlockParser + + redef type BLOCK: MdThematicBreak + redef var block = new MdThematicBreak(location, pattern) is lazy + + # Thematic break pattern + var pattern: String + + redef fun try_continue(state) do return null + + redef fun finalize(parser) do + super + + location.line_end = line_start + location.column_end = column_start + pattern.length - 1 + end +end + +# Thematic breaks parser factory +class MdThematicBreakParserFactory + super MdBlockQuoteParserFactory + + redef fun try_start(state, matched_block_parser) do + if state.indent >= 4 then return null + + var next_non_space = state.next_non_space_index + var line = state.line_string + var tbreak = line.substring(next_non_space, line.length - next_non_space).search(re_thematic_break) + if tbreak != null then + return (new MdBlockStart( + [new MdThematicBreakParser( + state.line, + state.column + 1, + next_non_space, + tbreak.to_s)] + )).at_index(line.length) + end + return null + end +end + +# Paragraphs parser +class MdParagraphParser + super MdBlockParser + + redef type BLOCK: MdParagraph + + redef var block = new MdParagraph(location) is lazy + + # Paragraph content + var content: nullable Buffer = new Buffer + + redef fun try_continue(state) do + if state.is_blank then return null + return new MdBlockContinue.at_index(state.index) + end + + redef fun add_line(line) do + var content = self.content + if content == null then return + if not content.is_empty then + content.add('\n') + end + content.append(line) + end + + redef fun finalize(parser) do + super + + var inline_parser = parser.inline_parser + var content = self.content + if content == null then return + + var content_string = content.to_s + var has_reference_defs = false + + var pos = inline_parser.parse_reference(content_string) + # try parsing the beginning as link reference definitions + while content_string.length > 3 and content_string.chars[0] == '[' and pos != 0 do + content_string = content_string.substring(pos, content_string.length - pos) + has_reference_defs = true + pos = inline_parser.parse_reference(content_string) + end + + if has_reference_defs and content_string.is_blank then + block.unlink + self.content = null + else + self.content = new Buffer.from_text(content_string) + end + end + + redef fun parse_inlines(inline_parser) do + var content = self.content + if content == null then return + inline_parser.parse(content.to_s, content_offset, block) + + var last_child = block.last_child + if last_child != null then + location.line_end = last_child.location.line_end + location.column_end = last_child.location.column_end + end + end +end + +# Html blocks parser +class MdHtmlBlockParser + super MdBlockParser + + redef type BLOCK: MdHtmlBlock + redef var block = new MdHtmlBlock(location) is lazy + + # Closing tag pattern + # + # Or null if the block is not closed + var closing_pattern: nullable Pattern + + # Is the current block finished? + var finished = false + + # Block content + var content = new Buffer + + redef fun try_continue(state) do + if finished then return null + + # blank lin ends type 6 and 7 blocks + if state.is_blank and closing_pattern == null then return null + + return new MdBlockContinue.at_index(state.index) + end + + redef fun add_line(line) do + if not content.is_empty then + content.add('\n') + end + content.append(line) + var closing_pattern = self.closing_pattern + if closing_pattern != null and line.has(closing_pattern) then + finished = true + end + end + + redef fun finalize(parser) do + super + + var content = self.content.to_s + block.literal = content + + var lines = content.split("\n") + location.line_end = location.line_start + lines.length - 1 + location.column_end = lines.last.length + end +end + +# Html blocks parser factory +class MdHtmlBlockParserFactory + super MdBlockParserFactory + + redef fun try_start(state, matched_block_parser) do + var next_non_space = state.next_non_space_index + var line = state.line_string + + if state.indent >= 4 or line.chars[next_non_space] != '<' then return null + + for block_type in [0..6] do + # type 7 can not interrupt a paragraph + if block_type == 6 and matched_block_parser.block isa MdParagraph then continue + var opener = re_html_blocks[block_type].first + var closer = re_html_blocks[block_type].last + if line.substring(next_non_space, line.length - next_non_space).has(opener.as(not null)) then + return (new MdBlockStart( + [new MdHtmlBlockParser( + state.line, + state.column + 1, + next_non_space, + closer)]) + ).at_index(state.index) + end + end + return null + end +end + +# Post Processing + +# Markdown post processor +# +# A Markdown AST visitor called after parsing from a MdParser +abstract class MdPostProcessor + super MdVisitor + + # Document behing processed + # + # Availlable only during a call to `post_process`. + var document: nullable MdDocument = null + + # Post process the `document` parsed by `parser` + fun post_process(parser: MdParser, document: MdDocument) do + self.document = document + enter_visit(document) + self.document = null + end + + # Call `MdNode::post_process` + redef fun visit(node) do node.post_process(self) +end + +redef class MdNode + + # Accept the visit of a `MdPostProcessor` + fun post_process(v: MdPostProcessor) do visit_all(v) +end + +# Utils + +redef class Sys + # ATX headings matching + private var re_atx_heading: Regex = "^(#\{1,6\})([ \t]+|$)".to_re + + # ATX trailings matching + private var re_atx_trailing: Regex = "(^|[ \t]+)#+[ \t]*$".to_re + + # SeText headings matching + private var re_setext_heading: Regex = "^(=+|-+)[ \t]*$".to_re + + # Blank lines matching + var re_trailing_blank_lines: Regex = "(\n[ \t]*)+$".to_re + + # Opening fence matching + var re_opening_fence: Regex = "^(`\{3,\})(.*)|^(~\{3,\})(.*)".to_re + + # Closing fence matching + var re_closing_fence: Regex = "^(`\{3,\}|~\{3,\})( *$)".to_re + + # List marker matching + var re_list_marker: Regex = "^([*+-])( |\t|$)|^([0-9]\{1,9\})([.)])( |\t|$)".to_re + + # Thematic break pattern + var re_thematic_break: Regex = "^((\\*[ \t]*)\{3,\}|(_[ \t]*)\{3,\}|(-[ \t]*)\{3,\})[ \t]*$".to_re + + # HTML blocks patterns + var re_html_blocks: Array[Array[nullable Regex]] do + var blocks = new Array[Array[nullable Regex]] + + var re0_opening = "^<(script|pre|style)(\\s|>|$)".to_re + re0_opening.ignore_case = true + var re0_closing = "".to_re + re0_closing.ignore_case = true + blocks.add([re0_opening, re0_closing]) + + blocks.add([ + "^".to_re + ]) + + blocks.add([ + "^<[?]".to_re, + "\\?>".to_re + ]) + + blocks.add([ + "^".to_re + ]) + + blocks.add([ + "^".to_re + ]) + + var re5_opening = "^]|$)".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 = "]" + 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 From b0303732ecb7833f8fc6f1b3988de1e522f12789 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:48:05 -0400 Subject: [PATCH 04/17] lib/markdown2: introduce markdown base test services Signed-off-by: Alexandre Terrasa --- lib/markdown2/tests/test_markdown.nit | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/markdown2/tests/test_markdown.nit diff --git a/lib/markdown2/tests/test_markdown.nit b/lib/markdown2/tests/test_markdown.nit new file mode 100644 index 0000000000..60f1f34536 --- /dev/null +++ b/lib/markdown2/tests/test_markdown.nit @@ -0,0 +1,42 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test suites for module `markdown` +module test_markdown + +import markdown_html_rendering + +# Abstract test class that instanciates the markdown parser +abstract class TestMarkdown + + # Markdown parser to use in tests + var md_parser = new MdParser + + # Parse a `md` string as a MdNode + fun parse_md(md: String): MdNode do return md_parser.parse(md) +end + +# Abstract test class that defines the test methods for HTML rendering +abstract class TestMarkdownHtml + super TestMarkdown + + # HTML renderer used in tests + var html_renderer = new HtmlRenderer + + # Render the `md` string as HTML + fun md_to_html(md: String): String do + var node = parse_md(md) + return html_renderer.render(node) + end +end From d01080a97f8b29e2d13aadc72959d1a1e18e430d Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:49:28 -0400 Subject: [PATCH 05/17] lib/markdown2: add tests for markdown nodes location Signed-off-by: Alexandre Terrasa --- .../tests/test_markdown_location.nit | 913 ++++++++++++++++++ 1 file changed, 913 insertions(+) create mode 100644 lib/markdown2/tests/test_markdown_location.nit diff --git a/lib/markdown2/tests/test_markdown_location.nit b/lib/markdown2/tests/test_markdown_location.nit new file mode 100644 index 0000000000..4aaa2184d2 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_location.nit @@ -0,0 +1,913 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown nodes location +module test_markdown_location is test + +import test_markdown + +abstract class TestMarkdownLocation + super TestMarkdown + + redef var md_parser do + var parser = super + parser.github_mode = true + parser.wikilinks_mode = true + return parser + end + + fun md_to_loc(md: String): String do + var node = parse_md(md) + var v = new TestMarkdownLocationVisitor + v.enter_visit(node) + return v.buffer.to_s + end +end + +class TestMarkdownLocationVisitor + super MdVisitor + + var buffer = new Buffer + var indent = 0 + + fun print_loc(node: MdNode) do + buffer.append "{" " * indent}{node.class_name}: {node.location}\n" + indent += 1 + node.visit_all(self) + indent -= 1 + end + + redef fun visit(node) do print_loc(node) +end + +class TestLocationOutput + super TestMarkdownLocation + test + + fun test_atx_headings1 is test do + var md = """ +# title 1 +## title 2 +### title 3 +#### title 4 +##### title 5 +###### title 6 +""" + var loc = """ +MdDocument: 1,1--6,14 + MdHeading: 1,1--1,9 + MdText: 1,3--1,9 + MdHeading: 2,1--2,10 + MdText: 2,4--2,10 + MdHeading: 3,1--3,11 + MdText: 3,5--3,11 + MdHeading: 4,1--4,12 + MdText: 4,6--4,12 + MdHeading: 5,1--5,13 + MdText: 5,7--5,13 + MdHeading: 6,1--6,14 + MdText: 6,8--6,14 +""" + assert md_to_loc(md) == loc + end + + fun test_atx_headings_with_trailings is test do + var md = """ +# title 1 # +## title 2 ## +### title 3 ### +#### title 4 #### +##### title 5 ##### +###### title 6 ###### +""" + var loc = """ +MdDocument: 1,1--6,21 + MdHeading: 1,1--1,11 + MdText: 1,3--1,9 + MdHeading: 2,1--2,13 + MdText: 2,4--2,10 + MdHeading: 3,1--3,15 + MdText: 3,5--3,11 + MdHeading: 4,1--4,17 + MdText: 4,6--4,12 + MdHeading: 5,1--5,19 + MdText: 5,7--5,13 + MdHeading: 6,1--6,21 + MdText: 6,8--6,14 +""" + assert md_to_loc(md) == loc + end + + fun test_settext_headings is test do + var md = """ +title 1 +======= + +title 2 +------- +""" + var loc = """ +MdDocument: 1,1--5,7 + MdHeading: 1,1--2,7 + MdText: 1,1--1,7 + MdHeading: 4,1--5,7 + MdText: 4,1--4,7 +""" + assert md_to_loc(md) == loc + end + + fun test_indented_code_spaces is test do + var md = """ + some code + + multi lines +""" + var loc = """ +MdDocument: 1,1--3,15 + MdIndentedCodeBlock: 1,1--3,15 +""" + assert md_to_loc(md) == loc + end + + fun test_indented_code_tabs is test do + var md = """ + some code + + multi lines +""" + var loc = """ +MdDocument: 1,1--3,15 + MdIndentedCodeBlock: 1,1--3,15 +""" + assert md_to_loc(md) == loc + end + + fun test_fenced_code is test do + var md = """ +~~~ +some code + +multi lines +~~~ +""" + var loc = """ +MdDocument: 1,1--5,3 + MdFencedCodeBlock: 1,1--5,3 +""" + assert md_to_loc(md) == loc + end + + fun test_thematic_breaks is test do + var md = """ +*** + +* * * + +* * * +""" + var loc = """ +MdDocument: 1,1--5,5 + MdThematicBreak: 1,1--1,3 + MdThematicBreak: 3,1--3,5 + MdThematicBreak: 5,1--5,5 +""" + assert md_to_loc(md) == loc + end + + fun test_html_blocks1 is test do + var md = """ +

bar

+ +
+ bar +

+""" + 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 . +""" + 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 . +""" + 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
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 *emph*. +""" + 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 From b82ea589c483137d71d3da54c5115b060999c0f7 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:57:20 -0400 Subject: [PATCH 06/17] lib/markdown2: introduce markdown rendering interfaces Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_rendering.nit | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 lib/markdown2/markdown_rendering.nit diff --git a/lib/markdown2/markdown_rendering.nit b/lib/markdown2/markdown_rendering.nit new file mode 100644 index 0000000000..ff49d1c57c --- /dev/null +++ b/lib/markdown2/markdown_rendering.nit @@ -0,0 +1,69 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown document rendering +module markdown_rendering + +import markdown_ast + +# Common interface for all markdown renderer +interface MdRenderer + super MdVisitor + + # Render `node` + fun render(node: MdNode): String is abstract +end + +# A renderer that output raw text +class RawTextVisitor + super MdRenderer + + # Text under construction + private var text: Buffer is noinit + + redef fun render(node) do + text = new Buffer + enter_visit(node) + return text.to_s + end + + # Append `string` to `text` + fun add(string: String) do text.append(string) + + redef fun visit(node) do node.render_raw_text(self) +end + +redef class MdNode + + # Return `self` as raw text + fun raw_text: String do + var v = new RawTextVisitor + return v.render(self) + end + + # Render `self` as raw text + fun render_raw_text(v: RawTextVisitor) do visit_all(v) +end + +redef class MdCode + redef fun render_raw_text(v) do v.add literal +end + +redef class MdLineBreak + redef fun render_raw_text(v) do v.add "\n" +end + +redef class MdText + redef fun render_raw_text(v) do v.add literal +end From 2b97cf3448b39d533cd4cc2057d5316ec3ef5ba6 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:52:35 -0400 Subject: [PATCH 07/17] lib/markdown2: introduce markdown rendering to HTML Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown2.nit | 27 + lib/markdown2/markdown_html_rendering.nit | 424 +++++++++++ lib/markdown2/tests/test_markdown_blocks.nit | 660 ++++++++++++++++++ .../tests/test_markdown_headings_id.nit | 61 ++ lib/markdown2/tests/test_markdown_inlines.nit | 335 +++++++++ 5 files changed, 1507 insertions(+) create mode 100644 lib/markdown2/markdown2.nit create mode 100644 lib/markdown2/markdown_html_rendering.nit create mode 100644 lib/markdown2/tests/test_markdown_blocks.nit create mode 100644 lib/markdown2/tests/test_markdown_headings_id.nit create mode 100644 lib/markdown2/tests/test_markdown_inlines.nit diff --git a/lib/markdown2/markdown2.nit b/lib/markdown2/markdown2.nit new file mode 100644 index 0000000000..85da1b96b6 --- /dev/null +++ b/lib/markdown2/markdown2.nit @@ -0,0 +1,27 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module markdown2 + +import markdown_block_parsing +import markdown_html_rendering + +redef class String + fun md_to_html2: String do + var parser = new MdParser + var doc = parser.parse(self) + var renderer = new HtmlRenderer + return renderer.render(doc) + end +end diff --git a/lib/markdown2/markdown_html_rendering.nit b/lib/markdown2/markdown_html_rendering.nit new file mode 100644 index 0000000000..b8a88e35d0 --- /dev/null +++ b/lib/markdown2/markdown_html_rendering.nit @@ -0,0 +1,424 @@ +# 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 + +# 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 "
" + v.add_line + visit_all(v) + v.add_line + v.add_raw "
" + v.add_line + end +end + +redef class MdCodeBlock + redef fun render_html(v) do + var info = self.info + v.add_line + v.add_raw "
"
+		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 ""
+		v.add_raw "
" + 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 "" + else + v.add_raw "" + end + visit_all(v) + v.add_raw "" + 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 "
    " + v.add_line + visit_all(v) + v.add_line + v.add_raw "
" + 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 "" + v.add_line + visit_all(v) + v.add_line + v.add_raw "" + v.add_line + end +end + +redef class MdListItem + redef fun render_html(v) do + v.add_raw "
  • " + visit_all(v) + v.add_raw "
  • " + 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 "

    " + end + visit_all(v) + if not is_tight then + v.add_raw "

    " + v.add_line + end + end +end + +redef class MdThematicBreak + redef fun render_html(v) do + v.add_line + v.add_raw "
    " + 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 "
    " + 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 "" + v.add_raw v.html_escape(literal, false) + v.add_raw "" + end +end + +redef class MdEmphasis + redef fun render_html(v) do + v.add_raw "" + visit_all(v) + v.add_raw "" + end +end + +redef class MdStrongEmphasis + redef fun render_html(v) do + v.add_raw "" + visit_all(v) + v.add_raw "" + 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 "" + 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 "" + visit_all(v) + v.add_raw "" + end +end + +redef class MdText + redef fun render_html(v) do + v.add_text literal + end +end diff --git a/lib/markdown2/tests/test_markdown_blocks.nit b/lib/markdown2/tests/test_markdown_blocks.nit new file mode 100644 index 0000000000..f81d225e47 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_blocks.nit @@ -0,0 +1,660 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either htmlress or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test for markdown blocks parsing +module test_markdown_blocks is test + +import test_markdown + +class TestMarkdownBlocks + super TestMarkdownHtml + test + + fun test_blocks_empty is test do + var md = "" + var html = "" + assert md_to_html(md) == html + end + + fun test_blocks_tabs is test do + var md = """\tsome code\n""" + var html = """
    some code\n
    \n""" + assert md_to_html(md) == html + end + + fun test_blocks_pagraph1 is test do + var md = "test\n" + var html = "

    test

    \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 = """

    line1\nline2

    \n

    line3 line4

    \n

    line5

    \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 = """ +

    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.

    +""" + 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 = """ +

    This is a H1

    +

    This is a 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 = """ +

    This is a H1

    +

    This is a H2

    +
    This is a 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 = """ +

    This is a H1

    +

    This is a H2

    +

    This is a H3

    +""" + assert md_to_html(md) == html + end + + fun test_blocks_hr1 is test do + var md = """ +* * * + +*** + +***** + +- - - + +--------------------------------------- +""" + var html = "
    \n
    \n
    \n
    \n
    \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 = """
    +

    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.

    +
    +""" + 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 = """
    +

    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.

    +
    +""" + 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 = """
    +

    This is the first level of quoting.

    +
    +

    This is nested blockquote.

    +
    +

    Back to the first level.

    +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list1 is test do + var md = """ +* Red +* Green +* Blue +""" + var html = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list2 is test do + var md = """ ++ Red ++ Green ++ Blue +""" + var html = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list3 is test do + var md = """ +- Red +- Green +- Blue +""" + var html = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list4 is test do + var md = """ +1. Bird +2. McHale +3. Parish +""" + var html = """
      +
    1. Bird
    2. +
    3. McHale
    4. +
    5. Parish
    6. +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list5 is test do + var md = """ +3. Bird +1. McHale +8. Parish +""" + var html = """
      +
    1. Bird
    2. +
    3. McHale
    4. +
    5. Parish
    6. +
    +""" + 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 = """ +
      +
    • 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.
    • +
    +""" + 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 = """ +
      +
    • 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.
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_list8 is test do + var md = """ +* Bird + +* Magic +""" + var html = """ +
      +
    • +

      Bird

      +
    • +
    • +

      Magic

      +
    • +
    +""" + 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 = """ +
      +
    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. +
    3. +

      Suspendisse id sem consectetuer libero luctus adipiscing.

      +
    4. +
    +""" + 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 = """ +
      +
    • +

      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.

      +
    • +
    +""" + 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 = """ +

    This is a paragraph

    +
      +
    • and this is a list
    • +
    +""" + 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 = """

    This is a normal paragraph:

    +
    This is a code block.
    +
    +""" + 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 + + +""" + var html = """ +

    Here is an example of AppleScript:

    +
    tell application "Foo"
    +    beep
    +end tell
    +
    +<div class="footer">
    +    &copy; 2004 Foo Corporation
    +</div>
    +
    +""" + 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 = """ +

    Here is an example of AppleScript:

    +
    beep
    +
    +""" + 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 + + +~~~ +""" + var html = """ +

    Here is an example of AppleScript:

    +
    tell application "Foo"
    +    beep
    +end tell
    +
    +<div class="footer">
    +    &copy; 2004 Foo Corporation
    +</div>
    +
    +""" + 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 + + +``` +""" + var html = """ +

    Here is an example of AppleScript:

    +
    tell application "Foo"
    +    beep
    +end tell
    +
    +<div class="footer">
    +    &copy; 2004 Foo Corporation
    +</div>
    +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_fenced_code3 is test do + var md = """ +```nit +print "Hello World!" +``` +""" + var html = """ +
    print "Hello World!"
    +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_fenced_code4 is test do + var md = """ +~~~ +print "Hello" +~~~ +~~~ +print "World" +~~~ +""" + var html = """ +
    print "Hello"
    +
    +
    print "World"
    +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_fenced_code5 is test do + var md = """ +~~~ +print "Hello" +~~~ +~~~ +print "World" +~~~ +""" + var html = """ +
    print "Hello"
    +
    +
    print "World"
    +
    +""" + 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 = """ +
    +

    This is a header.

    +
      +
    1. This is the first list item.
    2. +
    3. This is the second list item.
    4. +
    +

    Here's some example code:

    +
    return shell_exec("echo $input | $markdown_script");
    +
    +
    +""" + 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 = """ +
      +
    • +

      A list item with a blockquote:

      +
      +

      This is a blockquote +inside a list item.

      +
      +
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_nesting3 is test do + var md = """ +* A list item with a code block: + + +""" + var html = """ +
      +
    • +

      A list item with a code block:

      +
      <code goes here>
      +
      +
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_nesting4 is test do + var md = """ +* Tab + * Tab + * Tab +""" + var html = """ +
      +
    • Tab +
        +
      • Tab +
          +
        • Tab
        • +
        +
      • +
      +
    • +
    +""" + assert md_to_html(md) == html + end + + fun test_blocks_nesting5 is test do + var md = """ +* this + + * sub + + that +""" + var html = """ +
      +
    • +

      this

      +
        +
      • +

        sub

        +

        that

        +
      • +
      +
    • +
    +""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_markdown_headings_id.nit b/lib/markdown2/tests/test_markdown_headings_id.nit new file mode 100644 index 0000000000..28f91a3bd7 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_headings_id.nit @@ -0,0 +1,61 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test for markdown headings id generation +module test_markdown_headings_id is test + +import test_markdown + +class TestMarkdownHeadingsId + super TestMarkdownHtml + test + + redef var html_renderer = new HtmlRenderer(true) + + fun test_multiple_ids is test do + var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n""" + var html = """

    foo

    \n

    foo

    \n

    foo

    \n

    foo

    \n
    foo
    \n
    foo
    \n""" + assert md_to_html(md) == html + end + + fun test_escape_ids is test do + var md = """# foo *bar* \\*baz\\*\n""" + var html = """

    foo bar *baz*

    \n""" + assert md_to_html(md) == html + end + + fun test_escape_ids2 is test do + var md = """# foo#\n""" + var html = """

    foo#

    \n""" + assert md_to_html(md) == html + end + + fun test_avoid_spaces is test do + var md = """# foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test_remove_atx_trailing is test do + var md = """## foo ##\n ### bar ###\n""" + var html = """

    foo

    \n

    bar

    \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 = """

    foo ###

    \n

    foo ###

    \n

    foo #

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_markdown_inlines.nit b/lib/markdown2/tests/test_markdown_inlines.nit new file mode 100644 index 0000000000..6a2108410d --- /dev/null +++ b/lib/markdown2/tests/test_markdown_inlines.nit @@ -0,0 +1,335 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either htmlress or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown inline constructs +module test_markdown_inlines is test + +import test_markdown + +class TestMarkdownInlines + super TestMarkdownHtml + test + + fun test_inlines_emph1 is test do + var md = """ +*single asterisks* + +_single underscores_ + +**double asterisks** + +__double underscores__ +""" + var html = """

    single asterisks

    +

    single underscores

    +

    double asterisks

    +

    double underscores

    +""" + assert md_to_html(md) == html + end + + fun test_inlines_emph2 is test do + var md = "un*frigging*believable" + var html = "

    unfriggingbelievable

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_emph3 is test do + var md = "Con _cat_ this" + var html = "

    Con cat this

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_emph_ext is test do + var md = "Con_cat_this" + var html = "

    Con_cat_this

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_xml1 is test do + var md = """ +This is a regular paragraph. + + + + + +
    Foo
    + +This is another regular paragraph. +""" + var html = """ +

    This is a regular paragraph.

    + + + + +
    Foo
    +

    This is another regular paragraph.

    +""" + assert md_to_html(md) == html + end + + fun test_inlines_xml2 is test do + var md = """ +This is an image baz in a regular paragraph. +""" + var html = """ +

    This is an image baz in a regular paragraph.

    +""" + assert md_to_html(md) == html + end + + fun test_inlines_xml3 is test do + var md = """ +
    +""" + var html = """ +
    +""" + assert md_to_html(md) == html + end + + fun test_inlines_xml4 is test do + var md = """ +

    This is an example of a block element that should be escaped.

    +

    Idem for the second paragraph.

    +""" + assert md_to_html(md) == md + end + + fun test_inlines_xml5 is test do + var md = """ +# Some more XML tests + +

    This is an example of a block element that should be escaped.

    +

    Idem for the second paragraph.

    + +With a *md paragraph*! +""" + var html = """ +

    Some more XML tests

    +

    This is an example of a block element that should be escaped.

    +

    Idem for the second paragraph.

    +

    With a md paragraph!

    +""" + 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 = "

    -1 if < , +1 if > and 0 otherwise

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_span_code1 is test do + var md = "Use the `printf()` function." + var html = "

    Use the printf() function.

    \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 = "

    There is a literal backtick (`) here.

    \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 = """ +

    A single backtick in a code span: `

    +

    A backtick-delimited string in a code span: `foo`

    +""" + assert md_to_html(md) == html + end + + fun test_inlines_span_code4 is test do + var md = "Please don't use any `` tags." + var html = "

    Please don't use any <blink> tags.

    \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 = "

    &#8212; is the decimal-encoded equivalent of &mdash;.

    \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 = "

    *this text is surrounded by literal asterisks*

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_escape2 is test do + var md = "1986\\. What a great season." + var html = "

    1986. What a great season.

    \n" + assert md_to_html(md) == html + end + + fun test_inlines_escape3 is test do + var md = "Ben & Lux" + var html = "

    Ben & Lux

    \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 = """

    This is an example inline link.

    +

    This link has no title attribute.

    +""" + 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 = "

    See my About page for details.

    \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 = """ +

    This is an example reference-style link.

    +

    Some lorem ipsum

    +

    Some other lipsum

    +""" + 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 = """ +

    This is multiple examples: foo, bar, baz.

    +""" + 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 = """

    This is multiple examples: foo, bar, a.

    +""" + 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 = """

    I get 10 times more traffic from Google than from Yahoo or MSN.

    +""" + 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 = """

    Visit Daring Fireball for more information.

    +""" + 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 = """ +

    This one has a line +break.

    +

    This one has a line +break with a line-ending space.

    +""" + 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 = """ +

    Foo bar.

    +

    Foo bar.

    +""" + 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 = """ +

    Alt text

    +

    Alt text

    +""" + 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 = """ +

    Alt text

    +""" + assert md_to_html(md) == html + end +end From 2a0db6b437ecb15c767c44c2b6b7881bb42d38ea Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:48:40 -0400 Subject: [PATCH 08/17] lib/markdown2: add tests from issues Signed-off-by: Alexandre Terrasa --- lib/markdown2/tests/test_markdown_issues.nit | 152 +++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 lib/markdown2/tests/test_markdown_issues.nit diff --git a/lib/markdown2/tests/test_markdown_issues.nit b/lib/markdown2/tests/test_markdown_issues.nit new file mode 100644 index 0000000000..c8deb1b1e2 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_issues.nit @@ -0,0 +1,152 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests related to markdown issues from the Git repo +# +# Fixing: +# +# * 1525: lib/markdown: issue with nested fences +# * 2507: markdown: some lines are lost in verbatim blocks inside a list +# +# See . +module test_markdown_issues is test + +import test_markdown + +class TestMarkdownIssues + super TestMarkdownHtml + test + + # See . + fun test_issue_1525_1 is test do + var md = """ +A fence within a fence +~~~~~~ +some code: +~~~ +some other code +~~~ +~~~~~~ +""" + var html = """ +

    A fence within a fence

    +
    some code:
    +~~~
    +some other code
    +~~~
    +
    +""" + assert md_to_html(md) == html + end + + # See . + fun test_issue_1525_2 is test do + var md = """ +A fence within a fence +~~~ +some code: +``` +some other code +``` +~~~ +""" + var html = """ +

    A fence within a fence

    +
    some code:
    +```
    +some other code
    +```
    +
    +""" + assert md_to_html(md) == html + end + + # See . + fun test_issue_1525_3 is test do + var md = """ +A fence within a fence +``` +some code: +~~~ +some other code +~~~ +``` +""" + var html = """ +

    A fence within a fence

    +
    some code:
    +~~~
    +some other code
    +~~~
    +
    +""" + assert md_to_html(md) == html + end + + # See . + 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 = """ +
      +
    • 4 spaces, asdf and tab. are lost +
      a
      +as
      +asd
      +asdf
      +	tab.
      +asdfg
      +
      +
    • +
    +""" + assert md_to_html(md) == html + end + + # See . + fun test_issue_2507_2 is test do + var md = """ +* 2 spaces, `as` is lost + + ~~~ + a + as + asd + asdf + ~~~ +""" + var html = """ +
      +
    • +

      2 spaces, as is lost

      +
      a
      +as
      +asd
      +asdf
      +
      +
    • +
    +""" + assert md_to_html(md) == html + end +end From fec0f0b2f924ab921ed924e20c8d396f575efc62 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:48:19 -0400 Subject: [PATCH 09/17] lib/markdown2: import tests from DaringFireball Signed-off-by: Alexandre Terrasa --- lib/markdown2/tests/test_markdown_daring.nit | 1343 ++++++++++++++++++ 1 file changed, 1343 insertions(+) create mode 100644 lib/markdown2/tests/test_markdown_daring.nit diff --git a/lib/markdown2/tests/test_markdown_daring.nit b/lib/markdown2/tests/test_markdown_daring.nit new file mode 100644 index 0000000000..962e356abd --- /dev/null +++ b/lib/markdown2/tests/test_markdown_daring.nit @@ -0,0 +1,1343 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either htmlress or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown tests from DaringFireball +# +# See . +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](). + + +[1]: http://example.com/?foo=1&bar=2 +[2]: http://att.com/ "AT&T" +""" + + var html = """ +

    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 with an ampersand in the URL.

    +

    Here's a link with an ampersand in the link text: AT&T.

    +

    Here's an inline link.

    +

    Here's an inline link.

    +""" + assert md_to_html(md) == html + end + + fun test_daring_autolinks is test do + var md = """ +Link: . + +With an ampersand: + +* In a list? +* +* It should. + +> Blockquoted: + +Auto-links should not occur here: `` + + or here: +""" + + var html = """ +

    Link: http://example.com/.

    +

    With an ampersand: http://example.com/?foo=1&bar=2

    + +
    +

    Blockquoted: http://example.com/

    +
    +

    Auto-links should not occur here: <http://example.com/>

    +
    or here: <http://example.com/>
    +
    +""" + 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 bar. + +This is a tag with backslashes bar. +""" + var html = """ +

    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 bar.

    +

    This is a tag with backslashes bar.

    +""" + + 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 = """ +
    +

    Example:

    +
    sub status {
    +    print "working";
    +}
    +
    +

    Or:

    +
    sub status {
    +    return "working";
    +}
    +
    +
    +""" + 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 = """ +
    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
    +
    +""" + assert md_to_html(md) == html + end + + fun test_daring_rules is test do + var md = """ +Dashes: + +--- + + --- + + --- + + --- + + --- + +- - - + + - - - + + - - - + + - - - + + - - - + + +Asterisks: + +*** + + *** + + *** + + *** + + *** + +* * * + + * * * + + * * * + + * * * + + * * * + + +Underscores: + +___ + + ___ + + ___ + + ___ + + ___ + +_ _ _ + + _ _ _ + + _ _ _ + + _ _ _ + + _ _ _ +""" + + var html = """ +

    Dashes:

    +
    +
    +
    +
    +
    ---
    +
    +
    +
    +
    +
    +
    - - -
    +
    +

    Asterisks:

    +
    +
    +
    +
    +
    ***
    +
    +
    +
    +
    +
    +
    * * *
    +
    +

    Underscores:

    +
    +
    +
    +
    +
    ___
    +
    +
    +
    +
    +
    +
    _ _ _
    +
    +""" + assert md_to_html(md) == html + end + + fun test_daring_code_spans is test do + var md = """ +`` + +Fix for backticks within HTML tag: like this + +Here's how you put `` `backticks` `` in a code span. +""" + + var html = """ +

    <test a=" content of attribute ">

    +

    Fix for backticks within HTML tag: like this

    +

    Here's how you put `backticks` in a code span.

    +""" + 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]() + +![alt text]( "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 = """ +

    Alt text

    +

    Alt text

    +

    Inline within a paragraph: alt text.

    +

    alt text

    +

    alt text

    +

    alt text

    +

    alt text.

    +

    Empty

    +

    this is a stupid URL

    +

    alt text

    +

    alt text

    +""" + 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 w/ angle brackets + title]( "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 = """ +

    Just a URL.

    +

    URL and title.

    +

    URL and title.

    +

    URL and title.

    +

    URL and title.

    +

    URL wrapped in angle brackets.

    +

    URL w/ angle brackets + title.

    +

    Empty.

    +

    With parens in the URL

    +

    (With outer parens and parens in url)

    +

    With parens in the URL

    +

    (With outer parens and parens in url)

    +""" + 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 = """ +

    Foo bar.

    +

    Foo bar.

    +

    Foo bar.

    +

    With embedded [brackets].

    +

    Indented once.

    +

    Indented twice.

    +

    Indented thrice.

    +
    Indented [four][] times.
    +
    +
    [four]: /url
    +
    +
    +

    this should work

    +

    So should 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 points to something else.

    +

    Backslashing should suppress [this] and [this].

    +
    +

    Here's one where the link +breaks across lines.

    +

    Here's another where the link +breaks across lines, but with a line-ending space.

    +""" + 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 = """ +

    This is the simple case.

    +

    This one has a line +break.

    +

    This one has a line +break with a line-ending space.

    +

    this and the other

    +""" + assert md_to_html(md) == html + end + + fun test_daring_nested is test do + var md = """ +> foo +> +> > bar +> +> foo +""" + + var html = """ +
    +

    foo

    +
    +

    bar

    +
    +

    foo

    +
    +""" + 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 = """ +
    +

    A list within a blockquote:

    +
      +
    • asterisk 1
    • +
    • asterisk 2
    • +
    • asterisk 3
    • +
    +
    +""" + 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 = """ +

    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. +
    3. Second
    4. +
    5. Third
    6. +
    +

    and:

    +
      +
    1. One
    2. +
    3. Two
    4. +
    5. Three
    6. +
    +

    Loose using tabs:

    +
      +
    1. +

      First

      +
    2. +
    3. +

      Second

      +
    4. +
    5. +

      Third

      +
    6. +
    +

    and using spaces:

    +
      +
    1. +

      One

      +
    2. +
    3. +

      Two

      +
    4. +
    5. +

      Three

      +
    6. +
    +

    Multiple paragraphs:

    +
      +
    1. +

      Item 1, graf one.

      +

      Item 2. graf two. The quick brown fox jumped over the lazy dog's +back.

      +
    2. +
    3. +

      Item 2.

      +
    4. +
    5. +

      Item 3.

      +
    6. +
    +

    Nested

    +
      +
    • Tab +
        +
      • Tab +
          +
        • Tab
        • +
        +
      • +
      +
    • +
    +

    Here's another:

    +
      +
    1. First
    2. +
    3. Second: +
        +
      • Fee
      • +
      • Fie
      • +
      • Foe
      • +
      +
    4. +
    5. Third
    6. +
    +

    Same thing but with paragraphs:

    +
      +
    1. +

      First

      +
    2. +
    3. +

      Second:

      +
        +
      • Fee
      • +
      • Fie
      • +
      • Foe
      • +
      +
    4. +
    5. +

      Third

      +
    6. +
    +""" + 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 = """ +

    This is strong and em.

    +

    So is this word.

    +

    This is strong and em.

    +

    So is this word.

    +""" + 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 = """ +
      +
    • +

      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
    +
    +""" + assert md_to_html(md) == html + end + + fun test_daring_inline_html1 is test do + var md = """ +Here's a simple block: + +
    + foo +
    + +This should be a code block, though: + +
    + foo +
    + +As should this: + +
    foo
    + +Now, nested: + +
    +
    +
    + foo +
    +
    +
    + +This should just be an HTML comment: + + + +Multiline: + + + +Code block: + + + +Just plain comment, with trailing spaces on the line: + + + +Code: + +
    + +Hr's: + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +""" + + var html = """ +

    Here's a simple block:

    +
    + foo +
    +

    This should be a code block, though:

    +
    <div>
    +	foo
    +</div>
    +
    +

    As should this:

    +
    <div>foo</div>
    +
    +

    Now, nested:

    +
    +
    +
    + foo +
    +
    +
    +

    This should just be an HTML comment:

    + +

    Multiline:

    + +

    Code block:

    +
    <!-- Comment -->
    +
    +

    Just plain comment, with trailing spaces on the line:

    + +

    Code:

    +
    <hr />
    +
    +

    Hr's:

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +""" + assert md_to_html(md) == html + end + + fun test_daring_inline_html2 is test do + var md = """ +Simple block on one line: + +
    foo
    + +And nested without indentation: + +
    +
    +
    +foo +
    +
    +
    +
    bar
    +
    + +And with attributes: + +
    +
    +
    +
    + +This was broken in 1.0.2b7: + +
    +
    +foo +
    +
    +""" + + var html = """ +

    Simple block on one line:

    +
    foo
    +

    And nested without indentation:

    +
    +
    +
    +foo +
    +
    +
    +
    bar
    +
    +

    And with attributes:

    +
    +
    +
    +
    +

    This was broken in 1.0.2b7:

    +
    +
    +foo +
    +
    +""" + assert md_to_html(md) == html + end + + fun test_daring_inline_html3 is test do + var md = """ +Paragraph one. + + + + + +Paragraph two. + + + +The end. +""" + + var html = """ +

    Paragraph one.

    + + +

    Paragraph two.

    + +

    The end.

    +""" + assert md_to_html(md) == html + end +end From 824817b1491e5038bd7e0fe289849336199269df Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:56:40 -0400 Subject: [PATCH 10/17] lib/markdown2: import tests from CommonMark spec Signed-off-by: Alexandre Terrasa --- lib/markdown2/tests/commonmark_gen.nit | 251 ++++++ .../tests/test_commonmark_atx_headings.nit | 130 +++ .../tests/test_commonmark_autolinks.nit | 136 +++ .../test_commonmark_backslash_escapes.nit | 100 +++ .../tests/test_commonmark_blank_lines.nit | 28 + .../tests/test_commonmark_block_quotes.nit | 172 ++++ .../tests/test_commonmark_code_spans.nit | 124 +++ ...ommonmark_emphasis_and_strong_emphasis.nit | 796 ++++++++++++++++++ ...ntity_and_numeric_character_references.nit | 58 ++ .../test_commonmark_fenced_code_blocks.nit | 190 +++++ .../test_commonmark_hard_line_breaks.nit | 112 +++ .../tests/test_commonmark_html_blocks.nit | 280 ++++++ .../tests/test_commonmark_images.nit | 154 ++++ .../test_commonmark_indented_code_blocks.nit | 94 +++ .../tests/test_commonmark_inlines.nit | 28 + ..._commonmark_link_reference_definitions.nit | 154 ++++ lib/markdown2/tests/test_commonmark_links.nit | 514 +++++++++++ .../tests/test_commonmark_list_items.nit | 310 +++++++ lib/markdown2/tests/test_commonmark_lists.nit | 166 ++++ .../tests/test_commonmark_paragraphs.nit | 70 ++ .../tests/test_commonmark_precedence.nit | 28 + .../tests/test_commonmark_raw_html.nit | 148 ++++ .../tests/test_commonmark_setext_headings.nit | 178 ++++ .../test_commonmark_soft_line_breaks.nit | 34 + lib/markdown2/tests/test_commonmark_tabs.nit | 88 ++ .../tests/test_commonmark_textual_content.nit | 40 + .../tests/test_commonmark_thematic_breaks.nit | 136 +++ 27 files changed, 4519 insertions(+) create mode 100644 lib/markdown2/tests/commonmark_gen.nit create mode 100644 lib/markdown2/tests/test_commonmark_atx_headings.nit create mode 100644 lib/markdown2/tests/test_commonmark_autolinks.nit create mode 100644 lib/markdown2/tests/test_commonmark_backslash_escapes.nit create mode 100644 lib/markdown2/tests/test_commonmark_blank_lines.nit create mode 100644 lib/markdown2/tests/test_commonmark_block_quotes.nit create mode 100644 lib/markdown2/tests/test_commonmark_code_spans.nit create mode 100644 lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit create mode 100644 lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit create mode 100644 lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit create mode 100644 lib/markdown2/tests/test_commonmark_hard_line_breaks.nit create mode 100644 lib/markdown2/tests/test_commonmark_html_blocks.nit create mode 100644 lib/markdown2/tests/test_commonmark_images.nit create mode 100644 lib/markdown2/tests/test_commonmark_indented_code_blocks.nit create mode 100644 lib/markdown2/tests/test_commonmark_inlines.nit create mode 100644 lib/markdown2/tests/test_commonmark_link_reference_definitions.nit create mode 100644 lib/markdown2/tests/test_commonmark_links.nit create mode 100644 lib/markdown2/tests/test_commonmark_list_items.nit create mode 100644 lib/markdown2/tests/test_commonmark_lists.nit create mode 100644 lib/markdown2/tests/test_commonmark_paragraphs.nit create mode 100644 lib/markdown2/tests/test_commonmark_precedence.nit create mode 100644 lib/markdown2/tests/test_commonmark_raw_html.nit create mode 100644 lib/markdown2/tests/test_commonmark_setext_headings.nit create mode 100644 lib/markdown2/tests/test_commonmark_soft_line_breaks.nit create mode 100644 lib/markdown2/tests/test_commonmark_tabs.nit create mode 100644 lib/markdown2/tests/test_commonmark_textual_content.nit create mode 100644 lib/markdown2/tests/test_commonmark_thematic_breaks.nit diff --git a/lib/markdown2/tests/commonmark_gen.nit b/lib/markdown2/tests/commonmark_gen.nit new file mode 100644 index 0000000000..efebd807ca --- /dev/null +++ b/lib/markdown2/tests/commonmark_gen.nit @@ -0,0 +1,251 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generate Nitunit tests from commonmark specification. +# +# See the full specification and the test cases at . +# +# Usage: +# +# ~~~sh +# commonmark_gen +# ~~~ +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 " + exit 1 +end + +var gen = new TestGenerator(args.first, args.last) +gen.gen_tests diff --git a/lib/markdown2/tests/test_commonmark_atx_headings.nit b/lib/markdown2/tests/test_commonmark_atx_headings.nit new file mode 100644 index 0000000000..749ab182de --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_atx_headings.nit @@ -0,0 +1,130 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_atx_headings is test + +import test_markdown + +class TestCommonmarkATXHeadings + super TestMarkdownHtml + test + + fun test32 is test do + var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n""" + var html = """

    foo

    \n

    foo

    \n

    foo

    \n

    foo

    \n
    foo
    \n
    foo
    \n""" + assert md_to_html(md) == html + end + + fun test33 is test do + var md = """####### foo\n""" + var html = """

    ####### foo

    \n""" + assert md_to_html(md) == html + end + + fun test34 is test do + var md = """#5 bolt\n\n#hashtag\n""" + var html = """

    #5 bolt

    \n

    #hashtag

    \n""" + assert md_to_html(md) == html + end + + fun test35 is test do + var md = """\\## foo\n""" + var html = """

    ## foo

    \n""" + assert md_to_html(md) == html + end + + fun test36 is test do + var md = """# foo *bar* \\*baz\\*\n""" + var html = """

    foo bar *baz*

    \n""" + assert md_to_html(md) == html + end + + fun test37 is test do + var md = """# foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test38 is test do + var md = """ ### foo\n ## foo\n # foo\n""" + var html = """

    foo

    \n

    foo

    \n

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test39 is test do + var md = """ # foo\n""" + var html = """
    # foo\n
    \n""" + assert md_to_html(md) == html + end + + fun test40 is test do + var md = """foo\n # bar\n""" + var html = """

    foo\n# bar

    \n""" + assert md_to_html(md) == html + end + + fun test41 is test do + var md = """## foo ##\n ### bar ###\n""" + var html = """

    foo

    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test42 is test do + var md = """# foo ##################################\n##### foo ##\n""" + var html = """

    foo

    \n
    foo
    \n""" + assert md_to_html(md) == html + end + + fun test43 is test do + var md = """### foo ### \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test44 is test do + var md = """### foo ### b\n""" + var html = """

    foo ### b

    \n""" + assert md_to_html(md) == html + end + + fun test45 is test do + var md = """# foo#\n""" + var html = """

    foo#

    \n""" + assert md_to_html(md) == html + end + + fun test46 is test do + var md = """### foo \\###\n## foo #\\##\n# foo \\#\n""" + var html = """

    foo ###

    \n

    foo ###

    \n

    foo #

    \n""" + assert md_to_html(md) == html + end + + fun test47 is test do + var md = """****\n## foo\n****\n""" + var html = """
    \n

    foo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test48 is test do + var md = """Foo bar\n# baz\nBar foo\n""" + var html = """

    Foo bar

    \n

    baz

    \n

    Bar foo

    \n""" + assert md_to_html(md) == html + end + + fun test49 is test do + var md = """## \n#\n### ###\n""" + var html = """

    \n

    \n

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_autolinks.nit b/lib/markdown2/tests/test_commonmark_autolinks.nit new file mode 100644 index 0000000000..04c718c0fc --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_autolinks.nit @@ -0,0 +1,136 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_autolinks is test + +import test_markdown + +class TestCommonmarkAutolinks + super TestMarkdownHtml + test + + fun test568 is test do + var md = """\n""" + var html = """

    http://foo.bar.baz

    \n""" + assert md_to_html(md) == html + end + + fun test569 is test do + var md = """\n""" + var html = """

    http://foo.bar.baz/test?q=hello&id=22&boolean

    \n""" + assert md_to_html(md) == html + end + + fun test570 is test do + var md = """\n""" + var html = """

    irc://foo.bar:2233/baz

    \n""" + assert md_to_html(md) == html + end + + fun test571 is test do + var md = """\n""" + var html = """

    MAILTO:FOO@BAR.BAZ

    \n""" + assert md_to_html(md) == html + end + + fun test572 is test do + var md = """\n""" + var html = """

    a+b+c:d

    \n""" + assert md_to_html(md) == html + end + + fun test573 is test do + var md = """\n""" + var html = """

    made-up-scheme://foo,bar

    \n""" + assert md_to_html(md) == html + end + + fun test574 is test do + var md = """\n""" + var html = """

    http://../

    \n""" + assert md_to_html(md) == html + end + + fun test575 is test do + var md = """\n""" + var html = """

    localhost:5001/foo

    \n""" + assert md_to_html(md) == html + end + + fun test576 is test do + var md = """\n""" + var html = """

    <http://foo.bar/baz bim>

    \n""" + assert md_to_html(md) == html + end + + fun test577 is test do + var md = """\n""" + var html = """

    http://example.com/\\[\\

    \n""" + assert md_to_html(md) == html + end + + fun test578 is test do + var md = """\n""" + var html = """

    foo@bar.example.com

    \n""" + assert md_to_html(md) == html + end + + fun test579 is test do + var md = """\n""" + var html = """

    foo+special@Bar.baz-bar0.com

    \n""" + assert md_to_html(md) == html + end + + fun test580 is test do + var md = """\n""" + var html = """

    <foo+@bar.example.com>

    \n""" + assert md_to_html(md) == html + end + + fun test581 is test do + var md = """<>\n""" + var html = """

    <>

    \n""" + assert md_to_html(md) == html + end + + fun test582 is test do + var md = """< http://foo.bar >\n""" + var html = """

    < http://foo.bar >

    \n""" + assert md_to_html(md) == html + end + + fun test583 is test do + var md = """\n""" + var html = """

    <m:abc>

    \n""" + assert md_to_html(md) == html + end + + fun test584 is test do + var md = """\n""" + var html = """

    <foo.bar.baz>

    \n""" + assert md_to_html(md) == html + end + + fun test585 is test do + var md = """http://example.com\n""" + var html = """

    http://example.com

    \n""" + assert md_to_html(md) == html + end + + fun test586 is test do + var md = """foo@bar.example.com\n""" + var html = """

    foo@bar.example.com

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_backslash_escapes.nit b/lib/markdown2/tests/test_commonmark_backslash_escapes.nit new file mode 100644 index 0000000000..819158eb7e --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_backslash_escapes.nit @@ -0,0 +1,100 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_backslash_escapes is test + +import test_markdown + +class TestCommonmarkBackslashEscapes + super TestMarkdownHtml + test + + fun test291 is test do + var md = """\\!\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~\n""" + var html = """

    !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~

    \n""" + assert md_to_html(md) == html + end + + fun test292 is test do + var md = """\\\t\\A\\a\\ \\3\\φ\\«\n""" + var html = """

    \\\t\\A\\a\\ \\3\\φ\\«

    \n""" + assert md_to_html(md) == html + end + + fun test293 is test do + var md = """\\*not emphasized*\n\\
    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 = """

    *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""" + assert md_to_html(md) == html + end + + fun test294 is test do + var md = """\\\\*emphasis*\n""" + var html = """

    \\emphasis

    \n""" + assert md_to_html(md) == html + end + + fun test295 is test do + var md = """foo\\\nbar\n""" + var html = """

    foo
    \nbar

    \n""" + assert md_to_html(md) == html + end + + fun test296 is test do + var md = """`` \\[\\` ``\n""" + var html = """

    \\[\\`

    \n""" + assert md_to_html(md) == html + end + + fun test297 is test do + var md = """ \\[\\]\n""" + var html = """
    \\[\\]\n
    \n""" + assert md_to_html(md) == html + end + + fun test298 is test do + var md = """~~~\n\\[\\]\n~~~\n""" + var html = """
    \\[\\]\n
    \n""" + assert md_to_html(md) == html + end + + fun test299 is test do + var md = """\n""" + var html = """

    http://example.com?find=\\*

    \n""" + assert md_to_html(md) == html + end + + fun test300 is test do + var md = """\n""" + var html = """\n""" + assert md_to_html(md) == html + end + + fun test301 is test do + var md = """[foo](/bar\\* "ti\\*tle")\n""" + var html = """

    foo

    \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 = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test303 is test do + var md = """``` foo\\+bar\nfoo\n```\n""" + var html = """
    foo\n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_blank_lines.nit b/lib/markdown2/tests/test_commonmark_blank_lines.nit new file mode 100644 index 0000000000..f294684d89 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_blank_lines.nit @@ -0,0 +1,28 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_blank_lines is test + +import test_markdown + +class TestCommonmarkBlankLines + super TestMarkdownHtml + test + + fun test190 is test do + var md = """ \n\naaa\n \n\n# aaa\n\n \n""" + var html = """

    aaa

    \n

    aaa

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_block_quotes.nit b/lib/markdown2/tests/test_commonmark_block_quotes.nit new file mode 100644 index 0000000000..db9542f011 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_block_quotes.nit @@ -0,0 +1,172 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_block_quotes is test + +import test_markdown + +class TestCommonmarkBlockQuotes + super TestMarkdownHtml + test + + fun test191 is test do + var md = """> # Foo\n> bar\n> baz\n""" + var html = """
    \n

    Foo

    \n

    bar\nbaz

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test192 is test do + var md = """># Foo\n>bar\n> baz\n""" + var html = """
    \n

    Foo

    \n

    bar\nbaz

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test193 is test do + var md = """ > # Foo\n > bar\n > baz\n""" + var html = """
    \n

    Foo

    \n

    bar\nbaz

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test194 is test do + var md = """ > # Foo\n > bar\n > baz\n""" + var html = """
    > # Foo\n> bar\n> baz\n
    \n""" + assert md_to_html(md) == html + end + + fun test195 is test do + var md = """> # Foo\n> bar\nbaz\n""" + var html = """
    \n

    Foo

    \n

    bar\nbaz

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test196 is test do + var md = """> bar\nbaz\n> foo\n""" + var html = """
    \n

    bar\nbaz\nfoo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test197 is test do + var md = """> foo\n---\n""" + var html = """
    \n

    foo

    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test198 is test do + var md = """> - foo\n- bar\n""" + var html = """
    \n
      \n
    • foo
    • \n
    \n
    \n
      \n
    • bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test199 is test do + var md = """> foo\n bar\n""" + var html = """
    \n
    foo\n
    \n
    \n
    bar\n
    \n""" + assert md_to_html(md) == html + end + + fun test200 is test do + var md = """> ```\nfoo\n```\n""" + var html = """
    \n
    \n
    \n

    foo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test201 is test do + var md = """> foo\n - bar\n""" + var html = """
    \n

    foo\n- bar

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test202 is test do + var md = """>\n""" + var html = """
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test203 is test do + var md = """>\n> \n> \n""" + var html = """
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test204 is test do + var md = """>\n> foo\n> \n""" + var html = """
    \n

    foo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test205 is test do + var md = """> foo\n\n> bar\n""" + var html = """
    \n

    foo

    \n
    \n
    \n

    bar

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test206 is test do + var md = """> foo\n> bar\n""" + var html = """
    \n

    foo\nbar

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test207 is test do + var md = """> foo\n>\n> bar\n""" + var html = """
    \n

    foo

    \n

    bar

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test208 is test do + var md = """foo\n> bar\n""" + var html = """

    foo

    \n
    \n

    bar

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test209 is test do + var md = """> aaa\n***\n> bbb\n""" + var html = """
    \n

    aaa

    \n
    \n
    \n
    \n

    bbb

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test210 is test do + var md = """> bar\nbaz\n""" + var html = """
    \n

    bar\nbaz

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test211 is test do + var md = """> bar\n\nbaz\n""" + var html = """
    \n

    bar

    \n
    \n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test212 is test do + var md = """> bar\n>\nbaz\n""" + var html = """
    \n

    bar

    \n
    \n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test213 is test do + var md = """> > > foo\nbar\n""" + var html = """
    \n
    \n
    \n

    foo\nbar

    \n
    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test214 is test do + var md = """>>> foo\n> bar\n>>baz\n""" + var html = """
    \n
    \n
    \n

    foo\nbar\nbaz

    \n
    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test215 is test do + var md = """> code\n\n> not code\n""" + var html = """
    \n
    code\n
    \n
    \n
    \n

    not code

    \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_code_spans.nit b/lib/markdown2/tests/test_commonmark_code_spans.nit new file mode 100644 index 0000000000..edcf96b42e --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_code_spans.nit @@ -0,0 +1,124 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_code_spans is test + +import test_markdown + +class TestCommonmarkCodeSpans + super TestMarkdownHtml + test + + fun test316 is test do + var md = """`foo`\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test317 is test do + var md = """`` foo ` bar ``\n""" + var html = """

    foo ` bar

    \n""" + assert md_to_html(md) == html + end + + fun test318 is test do + var md = """` `` `\n""" + var html = """

    ``

    \n""" + assert md_to_html(md) == html + end + + fun test319 is test do + var md = """``\nfoo\n``\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test320 is test do + var md = """`foo bar\n baz`\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test321 is test do + var md = """`a  b`\n""" + var html = """

    a  b

    \n""" + assert md_to_html(md) == html + end + + fun test322 is test do + var md = """`foo `` bar`\n""" + var html = """

    foo `` bar

    \n""" + assert md_to_html(md) == html + end + + fun test323 is test do + var md = """`foo\\`bar`\n""" + var html = """

    foo\\bar`

    \n""" + assert md_to_html(md) == html + end + + fun test324 is test do + var md = """*foo`*`\n""" + var html = """

    *foo*

    \n""" + assert md_to_html(md) == html + end + + fun test325 is test do + var md = """[not a `link](/foo`)\n""" + var html = """

    [not a link](/foo)

    \n""" + assert md_to_html(md) == html + end + + fun test326 is test do + var md = """``\n""" + var html = """

    <a href="">`

    \n""" + assert md_to_html(md) == html + end + + fun test327 is test do + var md = """
    `\n""" + var html = """

    `

    \n""" + assert md_to_html(md) == html + end + + fun test328 is test do + var md = """``\n""" + var html = """

    <http://foo.bar.baz>`

    \n""" + assert md_to_html(md) == html + end + + fun test329 is test do + var md = """`\n""" + var html = """

    http://foo.bar.`baz`

    \n""" + assert md_to_html(md) == html + end + + fun test330 is test do + var md = """```foo``\n""" + var html = """

    ```foo``

    \n""" + assert md_to_html(md) == html + end + + fun test331 is test do + var md = """`foo\n""" + var html = """

    `foo

    \n""" + assert md_to_html(md) == html + end + + fun test332 is test do + var md = """`foo``bar``\n""" + var html = """

    `foobar

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit b/lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit new file mode 100644 index 0000000000..64e317b6cd --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_emphasis_and_strong_emphasis.nit @@ -0,0 +1,796 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_emphasis_and_strong_emphasis is test + +import test_markdown + +class TestCommonmarkEmphasisAndStrongEmphasis + super TestMarkdownHtml + test + + fun test333 is test do + var md = """*foo bar*\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test334 is test do + var md = """a * foo bar*\n""" + var html = """

    a * foo bar*

    \n""" + assert md_to_html(md) == html + end + + fun test335 is test do + var md = """a*"foo"*\n""" + var html = """

    a*"foo"*

    \n""" + assert md_to_html(md) == html + end + + fun test336 is test do + var md = """* a *\n""" + var html = """

    * a *

    \n""" + assert md_to_html(md) == html + end + + fun test337 is test do + var md = """foo*bar*\n""" + var html = """

    foobar

    \n""" + assert md_to_html(md) == html + end + + fun test338 is test do + var md = """5*6*78\n""" + var html = """

    5678

    \n""" + assert md_to_html(md) == html + end + + fun test339 is test do + var md = """_foo bar_\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test340 is test do + var md = """_ foo bar_\n""" + var html = """

    _ foo bar_

    \n""" + assert md_to_html(md) == html + end + + fun test341 is test do + var md = """a_"foo"_\n""" + var html = """

    a_"foo"_

    \n""" + assert md_to_html(md) == html + end + + fun test342 is test do + var md = """foo_bar_\n""" + var html = """

    foo_bar_

    \n""" + assert md_to_html(md) == html + end + + fun test343 is test do + var md = """5_6_78\n""" + var html = """

    5_6_78

    \n""" + assert md_to_html(md) == html + end + + fun test344 is test do + var md = """пристаням_стремятся_\n""" + var html = """

    пристаням_стремятся_

    \n""" + assert md_to_html(md) == html + end + + fun test345 is test do + var md = """aa_"bb"_cc\n""" + var html = """

    aa_"bb"_cc

    \n""" + assert md_to_html(md) == html + end + + fun test346 is test do + var md = """foo-_(bar)_\n""" + var html = """

    foo-(bar)

    \n""" + assert md_to_html(md) == html + end + + fun test347 is test do + var md = """_foo*\n""" + var html = """

    _foo*

    \n""" + assert md_to_html(md) == html + end + + fun test348 is test do + var md = """*foo bar *\n""" + var html = """

    *foo bar *

    \n""" + assert md_to_html(md) == html + end + + fun test349 is test do + var md = """*foo bar\n*\n""" + var html = """

    *foo bar\n*

    \n""" + assert md_to_html(md) == html + end + + fun test350 is test do + var md = """*(*foo)\n""" + var html = """

    *(*foo)

    \n""" + assert md_to_html(md) == html + end + + fun test351 is test do + var md = """*(*foo*)*\n""" + var html = """

    (foo)

    \n""" + assert md_to_html(md) == html + end + + fun test352 is test do + var md = """*foo*bar\n""" + var html = """

    foobar

    \n""" + assert md_to_html(md) == html + end + + fun test353 is test do + var md = """_foo bar _\n""" + var html = """

    _foo bar _

    \n""" + assert md_to_html(md) == html + end + + fun test354 is test do + var md = """_(_foo)\n""" + var html = """

    _(_foo)

    \n""" + assert md_to_html(md) == html + end + + fun test355 is test do + var md = """_(_foo_)_\n""" + var html = """

    (foo)

    \n""" + assert md_to_html(md) == html + end + + fun test356 is test do + var md = """_foo_bar\n""" + var html = """

    _foo_bar

    \n""" + assert md_to_html(md) == html + end + + fun test357 is test do + var md = """_пристаням_стремятся\n""" + var html = """

    _пристаням_стремятся

    \n""" + assert md_to_html(md) == html + end + + fun test358 is test do + var md = """_foo_bar_baz_\n""" + var html = """

    foo_bar_baz

    \n""" + assert md_to_html(md) == html + end + + fun test359 is test do + var md = """_(bar)_.\n""" + var html = """

    (bar).

    \n""" + assert md_to_html(md) == html + end + + fun test360 is test do + var md = """**foo bar**\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test361 is test do + var md = """** foo bar**\n""" + var html = """

    ** foo bar**

    \n""" + assert md_to_html(md) == html + end + + fun test362 is test do + var md = """a**"foo"**\n""" + var html = """

    a**"foo"**

    \n""" + assert md_to_html(md) == html + end + + fun test363 is test do + var md = """foo**bar**\n""" + var html = """

    foobar

    \n""" + assert md_to_html(md) == html + end + + fun test364 is test do + var md = """__foo bar__\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test365 is test do + var md = """__ foo bar__\n""" + var html = """

    __ foo bar__

    \n""" + assert md_to_html(md) == html + end + + fun test366 is test do + var md = """__\nfoo bar__\n""" + var html = """

    __\nfoo bar__

    \n""" + assert md_to_html(md) == html + end + + fun test367 is test do + var md = """a__"foo"__\n""" + var html = """

    a__"foo"__

    \n""" + assert md_to_html(md) == html + end + + fun test368 is test do + var md = """foo__bar__\n""" + var html = """

    foo__bar__

    \n""" + assert md_to_html(md) == html + end + + fun test369 is test do + var md = """5__6__78\n""" + var html = """

    5__6__78

    \n""" + assert md_to_html(md) == html + end + + fun test370 is test do + var md = """пристаням__стремятся__\n""" + var html = """

    пристаням__стремятся__

    \n""" + assert md_to_html(md) == html + end + + fun test371 is test do + var md = """__foo, __bar__, baz__\n""" + var html = """

    foo, bar, baz

    \n""" + assert md_to_html(md) == html + end + + fun test372 is test do + var md = """foo-__(bar)__\n""" + var html = """

    foo-(bar)

    \n""" + assert md_to_html(md) == html + end + + fun test373 is test do + var md = """**foo bar **\n""" + var html = """

    **foo bar **

    \n""" + assert md_to_html(md) == html + end + + fun test374 is test do + var md = """**(**foo)\n""" + var html = """

    **(**foo)

    \n""" + assert md_to_html(md) == html + end + + fun test375 is test do + var md = """*(**foo**)*\n""" + var html = """

    (foo)

    \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 = """

    Gomphocarpus (Gomphocarpus physocarpus, syn.\nAsclepias physocarpa)

    \n""" + assert md_to_html(md) == html + end + + fun test377 is test do + var md = """**foo "*bar*" foo**\n""" + var html = """

    foo "bar" foo

    \n""" + assert md_to_html(md) == html + end + + fun test378 is test do + var md = """**foo**bar\n""" + var html = """

    foobar

    \n""" + assert md_to_html(md) == html + end + + fun test379 is test do + var md = """__foo bar __\n""" + var html = """

    __foo bar __

    \n""" + assert md_to_html(md) == html + end + + fun test380 is test do + var md = """__(__foo)\n""" + var html = """

    __(__foo)

    \n""" + assert md_to_html(md) == html + end + + fun test381 is test do + var md = """_(__foo__)_\n""" + var html = """

    (foo)

    \n""" + assert md_to_html(md) == html + end + + fun test382 is test do + var md = """__foo__bar\n""" + var html = """

    __foo__bar

    \n""" + assert md_to_html(md) == html + end + + fun test383 is test do + var md = """__пристаням__стремятся\n""" + var html = """

    __пристаням__стремятся

    \n""" + assert md_to_html(md) == html + end + + fun test384 is test do + var md = """__foo__bar__baz__\n""" + var html = """

    foo__bar__baz

    \n""" + assert md_to_html(md) == html + end + + fun test385 is test do + var md = """__(bar)__.\n""" + var html = """

    (bar).

    \n""" + assert md_to_html(md) == html + end + + fun test386 is test do + var md = """*foo [bar](/url)*\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test387 is test do + var md = """*foo\nbar*\n""" + var html = """

    foo\nbar

    \n""" + assert md_to_html(md) == html + end + + fun test388 is test do + var md = """_foo __bar__ baz_\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test389 is test do + var md = """_foo _bar_ baz_\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test390 is test do + var md = """__foo_ bar_\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test391 is test do + var md = """*foo *bar**\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test392 is test do + var md = """*foo **bar** baz*\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test393 is test do + var md = """*foo**bar**baz*\n""" + var html = """

    foobarbaz

    \n""" + assert md_to_html(md) == html + end + + fun test394 is test do + var md = """*foo**bar*\n""" + var html = """

    foo**bar

    \n""" + assert md_to_html(md) == html + end + + fun test395 is test do + var md = """***foo** bar*\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test396 is test do + var md = """*foo **bar***\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test397 is test do + var md = """*foo**bar***\n""" + var html = """

    foobar

    \n""" + assert md_to_html(md) == html + end + + fun test398 is test do + var md = """*foo **bar *baz* bim** bop*\n""" + var html = """

    foo bar baz bim bop

    \n""" + assert md_to_html(md) == html + end + + fun test399 is test do + var md = """*foo [*bar*](/url)*\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test400 is test do + var md = """** is not an empty emphasis\n""" + var html = """

    ** is not an empty emphasis

    \n""" + assert md_to_html(md) == html + end + + fun test401 is test do + var md = """**** is not an empty strong emphasis\n""" + var html = """

    **** is not an empty strong emphasis

    \n""" + assert md_to_html(md) == html + end + + fun test402 is test do + var md = """**foo [bar](/url)**\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test403 is test do + var md = """**foo\nbar**\n""" + var html = """

    foo\nbar

    \n""" + assert md_to_html(md) == html + end + + fun test404 is test do + var md = """__foo _bar_ baz__\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test405 is test do + var md = """__foo __bar__ baz__\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test406 is test do + var md = """____foo__ bar__\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test407 is test do + var md = """**foo **bar****\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test408 is test do + var md = """**foo *bar* baz**\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test409 is test do + var md = """**foo*bar*baz**\n""" + var html = """

    foobarbaz

    \n""" + assert md_to_html(md) == html + end + + fun test410 is test do + var md = """***foo* bar**\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test411 is test do + var md = """**foo *bar***\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test412 is test do + var md = """**foo *bar **baz**\nbim* bop**\n""" + var html = """

    foo bar baz\nbim bop

    \n""" + assert md_to_html(md) == html + end + + fun test413 is test do + var md = """**foo [*bar*](/url)**\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test414 is test do + var md = """__ is not an empty emphasis\n""" + var html = """

    __ is not an empty emphasis

    \n""" + assert md_to_html(md) == html + end + + fun test415 is test do + var md = """____ is not an empty strong emphasis\n""" + var html = """

    ____ is not an empty strong emphasis

    \n""" + assert md_to_html(md) == html + end + + fun test416 is test do + var md = """foo ***\n""" + var html = """

    foo ***

    \n""" + assert md_to_html(md) == html + end + + fun test417 is test do + var md = """foo *\\**\n""" + var html = """

    foo *

    \n""" + assert md_to_html(md) == html + end + + fun test418 is test do + var md = """foo *_*\n""" + var html = """

    foo _

    \n""" + assert md_to_html(md) == html + end + + fun test419 is test do + var md = """foo *****\n""" + var html = """

    foo *****

    \n""" + assert md_to_html(md) == html + end + + fun test420 is test do + var md = """foo **\\***\n""" + var html = """

    foo *

    \n""" + assert md_to_html(md) == html + end + + fun test421 is test do + var md = """foo **_**\n""" + var html = """

    foo _

    \n""" + assert md_to_html(md) == html + end + + fun test422 is test do + var md = """**foo*\n""" + var html = """

    *foo

    \n""" + assert md_to_html(md) == html + end + + fun test423 is test do + var md = """*foo**\n""" + var html = """

    foo*

    \n""" + assert md_to_html(md) == html + end + + fun test424 is test do + var md = """***foo**\n""" + var html = """

    *foo

    \n""" + assert md_to_html(md) == html + end + + fun test425 is test do + var md = """****foo*\n""" + var html = """

    ***foo

    \n""" + assert md_to_html(md) == html + end + + fun test426 is test do + var md = """**foo***\n""" + var html = """

    foo*

    \n""" + assert md_to_html(md) == html + end + + fun test427 is test do + var md = """*foo****\n""" + var html = """

    foo***

    \n""" + assert md_to_html(md) == html + end + + fun test428 is test do + var md = """foo ___\n""" + var html = """

    foo ___

    \n""" + assert md_to_html(md) == html + end + + fun test429 is test do + var md = """foo _\\__\n""" + var html = """

    foo _

    \n""" + assert md_to_html(md) == html + end + + fun test430 is test do + var md = """foo _*_\n""" + var html = """

    foo *

    \n""" + assert md_to_html(md) == html + end + + fun test431 is test do + var md = """foo _____\n""" + var html = """

    foo _____

    \n""" + assert md_to_html(md) == html + end + + fun test432 is test do + var md = """foo __\\___\n""" + var html = """

    foo _

    \n""" + assert md_to_html(md) == html + end + + fun test433 is test do + var md = """foo __*__\n""" + var html = """

    foo *

    \n""" + assert md_to_html(md) == html + end + + fun test434 is test do + var md = """__foo_\n""" + var html = """

    _foo

    \n""" + assert md_to_html(md) == html + end + + fun test435 is test do + var md = """_foo__\n""" + var html = """

    foo_

    \n""" + assert md_to_html(md) == html + end + + fun test436 is test do + var md = """___foo__\n""" + var html = """

    _foo

    \n""" + assert md_to_html(md) == html + end + + fun test437 is test do + var md = """____foo_\n""" + var html = """

    ___foo

    \n""" + assert md_to_html(md) == html + end + + fun test438 is test do + var md = """__foo___\n""" + var html = """

    foo_

    \n""" + assert md_to_html(md) == html + end + + fun test439 is test do + var md = """_foo____\n""" + var html = """

    foo___

    \n""" + assert md_to_html(md) == html + end + + fun test440 is test do + var md = """**foo**\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test441 is test do + var md = """*_foo_*\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test442 is test do + var md = """__foo__\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test443 is test do + var md = """_*foo*_\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test444 is test do + var md = """****foo****\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test445 is test do + var md = """____foo____\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test446 is test do + var md = """******foo******\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test447 is test do + var md = """***foo***\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test448 is test do + var md = """_____foo_____\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test449 is test do + var md = """*foo _bar* baz_\n""" + var html = """

    foo _bar baz_

    \n""" + assert md_to_html(md) == html + end + + fun test450 is test do + var md = """*foo __bar *baz bim__ bam*\n""" + var html = """

    foo bar *baz bim bam

    \n""" + assert md_to_html(md) == html + end + + fun test451 is test do + var md = """**foo **bar baz**\n""" + var html = """

    **foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test452 is test do + var md = """*foo *bar baz*\n""" + var html = """

    *foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test453 is test do + var md = """*[bar*](/url)\n""" + var html = """

    *bar*

    \n""" + assert md_to_html(md) == html + end + + fun test454 is test do + var md = """_foo [bar_](/url)\n""" + var html = """

    _foo bar_

    \n""" + assert md_to_html(md) == html + end + + fun test455 is test do + var md = """*\n""" + var html = """

    *

    \n""" + assert md_to_html(md) == html + end + + fun test456 is test do + var md = """**\n""" + var html = """

    **

    \n""" + assert md_to_html(md) == html + end + + fun test457 is test do + var md = """__\n""" + var html = """

    __

    \n""" + assert md_to_html(md) == html + end + + fun test458 is test do + var md = """*a `*`*\n""" + var html = """

    a *

    \n""" + assert md_to_html(md) == html + end + + fun test459 is test do + var md = """_a `_`_\n""" + var html = """

    a _

    \n""" + assert md_to_html(md) == html + end + + fun test460 is test do + var md = """**a\n""" + var html = """

    **ahttp://foo.bar/?q=**

    \n""" + assert md_to_html(md) == html + end + + fun test461 is test do + var md = """__a\n""" + var html = """

    __ahttp://foo.bar/?q=__

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit b/lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit new file mode 100644 index 0000000000..f61edd49fe --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_entity_and_numeric_character_references.nit @@ -0,0 +1,58 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_entity_and_numeric_character_references is test + +import test_markdown + +class TestCommonmarkEntityAndNumericCharacterReferences + super TestMarkdownHtml + test + + fun test307 is test do + var md = """  &x; &#; &#x;\n�\n&#abcdef0;\n&ThisIsNotDefined; &hi?;\n""" + var html = """

    &nbsp &x; &#; &#x;\n&#987654321;\n&#abcdef0;\n&ThisIsNotDefined; &hi?;

    \n""" + assert md_to_html(md) == html + end + + fun test308 is test do + var md = """©\n""" + var html = """

    &copy

    \n""" + assert md_to_html(md) == html + end + + fun test309 is test do + var md = """&MadeUpEntity;\n""" + var html = """

    &MadeUpEntity;

    \n""" + assert md_to_html(md) == html + end + + fun test310 is test do + var md = """\n""" + var html = """\n""" + assert md_to_html(md) == html + end + + fun test314 is test do + var md = """`föö`\n""" + var html = """

    f&ouml;&ouml;

    \n""" + assert md_to_html(md) == html + end + + fun test315 is test do + var md = """ föfö\n""" + var html = """
    f&ouml;f&ouml;\n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit b/lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit new file mode 100644 index 0000000000..2d7072ae05 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_fenced_code_blocks.nit @@ -0,0 +1,190 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_fenced_code_blocks is test + +import test_markdown + +class TestCommonmarkFencedCodeBlocks + super TestMarkdownHtml + test + + fun test88 is test do + var md = """```\n<\n >\n```\n""" + var html = """
    <\n >\n
    \n""" + assert md_to_html(md) == html + end + + fun test89 is test do + var md = """~~~\n<\n >\n~~~\n""" + var html = """
    <\n >\n
    \n""" + assert md_to_html(md) == html + end + + fun test90 is test do + var md = """``\nfoo\n``\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test91 is test do + var md = """```\naaa\n~~~\n```\n""" + var html = """
    aaa\n~~~\n
    \n""" + assert md_to_html(md) == html + end + + fun test92 is test do + var md = """~~~\naaa\n```\n~~~\n""" + var html = """
    aaa\n```\n
    \n""" + assert md_to_html(md) == html + end + + fun test93 is test do + var md = """````\naaa\n```\n``````\n""" + var html = """
    aaa\n```\n
    \n""" + assert md_to_html(md) == html + end + + fun test94 is test do + var md = """~~~~\naaa\n~~~\n~~~~\n""" + var html = """
    aaa\n~~~\n
    \n""" + assert md_to_html(md) == html + end + + fun test95 is test do + var md = """```\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test96 is test do + var md = """`````\n\n```\naaa\n""" + var html = """
    \n```\naaa\n
    \n""" + assert md_to_html(md) == html + end + + fun test97 is test do + var md = """> ```\n> aaa\n\nbbb\n""" + var html = """
    \n
    aaa\n
    \n
    \n

    bbb

    \n""" + assert md_to_html(md) == html + end + + fun test98 is test do + var md = """```\n\n \n```\n""" + var html = """
    \n  \n
    \n""" + assert md_to_html(md) == html + end + + fun test99 is test do + var md = """```\n```\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test100 is test do + var md = """ ```\n aaa\naaa\n```\n""" + var html = """
    aaa\naaa\n
    \n""" + assert md_to_html(md) == html + end + + fun test101 is test do + var md = """ ```\naaa\n aaa\naaa\n ```\n""" + var html = """
    aaa\naaa\naaa\n
    \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 = """
    aaa\n aaa\naaa\n
    \n""" + assert md_to_html(md) == html + end + + fun test103 is test do + var md = """ ```\n aaa\n ```\n""" + var html = """
    ```\naaa\n```\n
    \n""" + assert md_to_html(md) == html + end + + fun test104 is test do + var md = """```\naaa\n ```\n""" + var html = """
    aaa\n
    \n""" + assert md_to_html(md) == html + end + + fun test105 is test do + var md = """ ```\naaa\n ```\n""" + var html = """
    aaa\n
    \n""" + assert md_to_html(md) == html + end + + fun test106 is test do + var md = """```\naaa\n ```\n""" + var html = """
    aaa\n    ```\n
    \n""" + assert md_to_html(md) == html + end + + fun test107 is test do + var md = """``` ```\naaa\n""" + var html = """

    \naaa

    \n""" + assert md_to_html(md) == html + end + + fun test108 is test do + var md = """~~~~~~\naaa\n~~~ ~~\n""" + var html = """
    aaa\n~~~ ~~\n
    \n""" + assert md_to_html(md) == html + end + + fun test109 is test do + var md = """foo\n```\nbar\n```\nbaz\n""" + var html = """

    foo

    \n
    bar\n
    \n

    baz

    \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 = """

    foo

    \n
    bar\n
    \n

    baz

    \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 = """
    def foo(x)\n  return 3\nend\n
    \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 = """
    def foo(x)\n  return 3\nend\n
    \n""" + assert md_to_html(md) == html + end + + fun test113 is test do + var md = """````;\n````\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test114 is test do + var md = """``` aa ```\nfoo\n""" + var html = """

    aa\nfoo

    \n""" + assert md_to_html(md) == html + end + + fun test115 is test do + var md = """```\n``` aaa\n```\n""" + var html = """
    ``` aaa\n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_hard_line_breaks.nit b/lib/markdown2/tests/test_commonmark_hard_line_breaks.nit new file mode 100644 index 0000000000..7e2336f7e3 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_hard_line_breaks.nit @@ -0,0 +1,112 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_hard_line_breaks is test + +import test_markdown + +class TestCommonmarkHardLineBreaks + super TestMarkdownHtml + test + + fun test608 is test do + var md = """foo \nbaz\n""" + var html = """

    foo
    \nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test609 is test do + var md = """foo\\\nbaz\n""" + var html = """

    foo
    \nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test610 is test do + var md = """foo \nbaz\n""" + var html = """

    foo
    \nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test611 is test do + var md = """foo \n bar\n""" + var html = """

    foo
    \nbar

    \n""" + assert md_to_html(md) == html + end + + fun test612 is test do + var md = """foo\\\n bar\n""" + var html = """

    foo
    \nbar

    \n""" + assert md_to_html(md) == html + end + + fun test613 is test do + var md = """*foo \nbar*\n""" + var html = """

    foo
    \nbar

    \n""" + assert md_to_html(md) == html + end + + fun test614 is test do + var md = """*foo\\\nbar*\n""" + var html = """

    foo
    \nbar

    \n""" + assert md_to_html(md) == html + end + + fun test615 is test do + var md = """`code \nspan`\n""" + var html = """

    code span

    \n""" + assert md_to_html(md) == html + end + + fun test616 is test do + var md = """`code\\\nspan`\n""" + var html = """

    code\\ span

    \n""" + assert md_to_html(md) == html + end + + fun test617 is test do + var md = """
    \n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test618 is test do + var md = """\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test619 is test do + var md = """foo\\\n""" + var html = """

    foo\\

    \n""" + assert md_to_html(md) == html + end + + fun test620 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test621 is test do + var md = """### foo\\\n""" + var html = """

    foo\\

    \n""" + assert md_to_html(md) == html + end + + fun test622 is test do + var md = """### foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_html_blocks.nit b/lib/markdown2/tests/test_commonmark_html_blocks.nit new file mode 100644 index 0000000000..5b925555f3 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_html_blocks.nit @@ -0,0 +1,280 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_html_blocks is test + +import test_markdown + +class TestCommonmarkHTMLBlocks + super TestMarkdownHtml + test + + fun test116 is test do + var md = """
    \n
    \n**Hello**,\n\n_world_.\n
    \n
    \n""" + var html = """
    \n
    \n**Hello**,\n

    world.\n

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test117 is test do + var md = """\n \n \n \n
    \n hi\n
    \n\nokay.\n""" + var html = """\n \n \n \n
    \n hi\n
    \n

    okay.

    \n""" + assert md_to_html(md) == html + end + + fun test118 is test do + var md = """
    \n*foo*\n""" + assert md_to_html(md) == html + end + + fun test120 is test do + var md = """
    \n\n*Markdown*\n\n
    \n""" + var html = """
    \n

    Markdown

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test121 is test do + var md = """
    \n
    \n""" + var html = """
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test122 is test do + var md = """
    \n
    \n""" + var html = """
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test123 is test do + var md = """
    \n*foo*\n\n*bar*\n""" + var html = """
    \n*foo*\n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test124 is test do + var md = """
    \n""" + var html = """\n""" + assert md_to_html(md) == html + end + + fun test128 is test do + var md = """
    \nfoo\n
    \n""" + var html = """
    \nfoo\n
    \n""" + assert md_to_html(md) == html + end + + fun test129 is test do + var md = """
    \n``` c\nint x = 33;\n```\n""" + var html = """
    \n``` c\nint x = 33;\n```\n""" + assert md_to_html(md) == html + end + + fun test130 is test do + var md = """\n*bar*\n\n""" + var html = """\n*bar*\n\n""" + assert md_to_html(md) == html + end + + fun test131 is test do + var md = """\n*bar*\n\n""" + var html = """\n*bar*\n\n""" + assert md_to_html(md) == html + end + + fun test132 is test do + var md = """\n*bar*\n\n""" + var html = """\n*bar*\n\n""" + assert md_to_html(md) == html + end + + fun test133 is test do + var md = """\n*bar*\n""" + var html = """\n*bar*\n""" + assert md_to_html(md) == html + end + + fun test134 is test do + var md = """\n*foo*\n\n""" + var html = """\n*foo*\n\n""" + assert md_to_html(md) == html + end + + fun test135 is test do + var md = """\n\n*foo*\n\n\n""" + var html = """\n

    foo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test136 is test do + var md = """*foo*\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test137 is test do + var md = """
    \nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n
    \nokay\n""" + var html = """
    \nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n
    \n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test138 is test do + var md = """\nokay\n""" + var html = """\n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test139 is test do + var md = """\nh1 {color:red;}\n\np {color:blue;}\n\nokay\n""" + var html = """\nh1 {color:red;}\n\np {color:blue;}\n\n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test140 is test do + var md = """\n\nfoo\n""" + var html = """\n\nfoo\n""" + assert md_to_html(md) == html + end + + fun test141 is test do + var md = """>
    \n> foo\n\nbar\n""" + var html = """
    \n
    \nfoo\n
    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test142 is test do + var md = """-
    \n- foo\n""" + var html = """
      \n
    • \n
      \n
    • \n
    • foo
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test143 is test do + var md = """\n*foo*\n""" + var html = """\n

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test144 is test do + var md = """*bar*\n*baz*\n""" + var html = """*bar*\n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test145 is test do + var md = """1. *bar*\n""" + var html = """1. *bar*\n""" + assert md_to_html(md) == html + end + + fun test146 is test do + var md = """\nokay\n""" + var html = """\n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test147 is test do + var md = """';\n\n?>\nokay\n""" + var html = """';\n\n?>\n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test148 is test do + var md = """\n""" + var html = """\n""" + assert md_to_html(md) == html + end + + fun test149 is test do + var md = """\nokay\n""" + var html = """\n

    okay

    \n""" + assert md_to_html(md) == html + end + + fun test150 is test do + var md = """ \n\n \n""" + var html = """ \n
    <!-- foo -->\n
    \n""" + assert md_to_html(md) == html + end + + fun test151 is test do + var md = """
    \n\n
    \n""" + var html = """
    \n
    <div>\n
    \n""" + assert md_to_html(md) == html + end + + fun test152 is test do + var md = """Foo\n
    \nbar\n
    \n""" + var html = """

    Foo

    \n
    \nbar\n
    \n""" + assert md_to_html(md) == html + end + + fun test153 is test do + var md = """
    \nbar\n
    \n*foo*\n""" + var html = """
    \nbar\n
    \n*foo*\n""" + assert md_to_html(md) == html + end + + fun test154 is test do + var md = """Foo\n\nbaz\n""" + var html = """

    Foo\n\nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test155 is test do + var md = """
    \n\n*Emphasized* text.\n\n
    \n""" + var html = """
    \n

    Emphasized text.

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test156 is test do + var md = """
    \n*Emphasized* text.\n
    \n""" + var html = """
    \n*Emphasized* text.\n
    \n""" + assert md_to_html(md) == html + end + + fun test157 is test do + var md = """\n\n\n\n\n\n\n\n
    \nHi\n
    \n""" + var html = """\n\n\n\n
    \nHi\n
    \n""" + assert md_to_html(md) == html + end + + fun test158 is test do + var md = """\n\n \n\n \n\n \n\n
    \n Hi\n
    \n""" + var html = """\n \n
    <td>\n  Hi\n</td>\n
    \n \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_images.nit b/lib/markdown2/tests/test_commonmark_images.nit new file mode 100644 index 0000000000..e75f4c171e --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_images.nit @@ -0,0 +1,154 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_images is test + +import test_markdown + +class TestCommonmarkImages + super TestMarkdownHtml + test + + fun test546 is test do + var md = """![foo](/url "title")\n""" + var html = """

    foo

    \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 = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test548 is test do + var md = """![foo ![bar](/url)](/url2)\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test549 is test do + var md = """![foo [bar](/url)](/url2)\n""" + var html = """

    foo bar

    \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 = """

    foo bar

    \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 = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test552 is test do + var md = """![foo](train.jpg)\n""" + var html = """

    foo

    \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 = """

    My foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test554 is test do + var md = """![foo]()\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test555 is test do + var md = """![](/url)\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test556 is test do + var md = """![foo][bar]\n\n[bar]: /url\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test557 is test do + var md = """![foo][bar]\n\n[BAR]: /url\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test558 is test do + var md = """![foo][]\n\n[foo]: /url "title"\n""" + var html = """

    foo

    \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 = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test560 is test do + var md = """![Foo][]\n\n[foo]: /url "title"\n""" + var html = """

    Foo

    \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 = """

    foo\n[]

    \n""" + assert md_to_html(md) == html + end + + fun test562 is test do + var md = """![foo]\n\n[foo]: /url "title"\n""" + var html = """

    foo

    \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 = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test564 is test do + var md = """![[foo]]\n\n[[foo]]: /url "title"\n""" + var html = """

    ![[foo]]

    \n

    [[foo]]: /url "title"

    \n""" + assert md_to_html(md) == html + end + + fun test565 is test do + var md = """![Foo]\n\n[foo]: /url "title"\n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test566 is test do + var md = """!\\[foo]\n\n[foo]: /url "title"\n""" + var html = """

    ![foo]

    \n""" + assert md_to_html(md) == html + end + + fun test567 is test do + var md = """\\![foo]\n\n[foo]: /url "title"\n""" + var html = """

    !foo

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_indented_code_blocks.nit b/lib/markdown2/tests/test_commonmark_indented_code_blocks.nit new file mode 100644 index 0000000000..0c8bf2f310 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_indented_code_blocks.nit @@ -0,0 +1,94 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_indented_code_blocks is test + +import test_markdown + +class TestCommonmarkIndentedCodeBlocks + super TestMarkdownHtml + test + + fun test76 is test do + var md = """ a simple\n indented code block\n""" + var html = """
    a simple\n  indented code block\n
    \n""" + assert md_to_html(md) == html + end + + fun test77 is test do + var md = """ - foo\n\n bar\n""" + var html = """
      \n
    • \n

      foo

      \n

      bar

      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test78 is test do + var md = """1. foo\n\n - bar\n""" + var html = """
      \n
    1. \n

      foo

      \n
        \n
      • bar
      • \n
      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test79 is test do + var md = """ \n *hi*\n\n - one\n""" + var html = """
    <a/>\n*hi*\n\n- one\n
    \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 = """
    chunk1\n\nchunk2\n\n\n\nchunk3\n
    \n""" + assert md_to_html(md) == html + end + + fun test81 is test do + var md = """ chunk1\n \n chunk2\n""" + var html = """
    chunk1\n  \n  chunk2\n
    \n""" + assert md_to_html(md) == html + end + + fun test82 is test do + var md = """Foo\n bar\n\n""" + var html = """

    Foo\nbar

    \n""" + assert md_to_html(md) == html + end + + fun test83 is test do + var md = """ foo\nbar\n""" + var html = """
    foo\n
    \n

    bar

    \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 = """

    Heading

    \n
    foo\n
    \n

    Heading

    \n
    foo\n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test85 is test do + var md = """ foo\n bar\n""" + var html = """
        foo\nbar\n
    \n""" + assert md_to_html(md) == html + end + + fun test86 is test do + var md = """\n \n foo\n \n\n""" + var html = """
    foo\n
    \n""" + assert md_to_html(md) == html + end + + fun test87 is test do + var md = """ foo \n""" + var html = """
    foo  \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_inlines.nit b/lib/markdown2/tests/test_commonmark_inlines.nit new file mode 100644 index 0000000000..ac1315663f --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_inlines.nit @@ -0,0 +1,28 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_inlines is test + +import test_markdown + +class TestCommonmarkInlines + super TestMarkdownHtml + test + + fun test290 is test do + var md = """`hi`lo`\n""" + var html = """

    hilo`

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_link_reference_definitions.nit b/lib/markdown2/tests/test_commonmark_link_reference_definitions.nit new file mode 100644 index 0000000000..ebd19a4dcb --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_link_reference_definitions.nit @@ -0,0 +1,154 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_link_reference_definitions is test + +import test_markdown + +class TestCommonmarkLinkReferenceDefinitions + super TestMarkdownHtml + test + + fun test159 is test do + var md = """[foo]: /url "title"\n\n[foo]\n""" + var html = """

    foo

    \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 = """

    foo

    \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 = """

    Foo*bar]

    \n""" + assert md_to_html(md) == html + end + + fun test162 is test do + var md = """[Foo bar]:\n\n'title'\n\n[Foo bar]\n""" + var html = """

    Foo bar

    \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 = """

    foo

    \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 = """

    [foo]: /url 'title

    \n

    with blank line'

    \n

    [foo]

    \n""" + assert md_to_html(md) == html + end + + fun test165 is test do + var md = """[foo]:\n/url\n\n[foo]\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test166 is test do + var md = """[foo]:\n\n[foo]\n""" + var html = """

    [foo]:

    \n

    [foo]

    \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 = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test168 is test do + var md = """[foo]\n\n[foo]: url\n""" + var html = """

    foo

    \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 = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test170 is test do + var md = """[FOO]: /url\n\n[Foo]\n""" + var html = """

    Foo

    \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 = """

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test174 is test do + var md = """[foo]: /url "title" ok\n""" + var html = """

    [foo]: /url "title" ok

    \n""" + assert md_to_html(md) == html + end + + fun test175 is test do + var md = """[foo]: /url\n"title" ok\n""" + var html = """

    "title" ok

    \n""" + assert md_to_html(md) == html + end + + fun test176 is test do + var md = """ [foo]: /url "title"\n\n[foo]\n""" + var html = """
    [foo]: /url "title"\n
    \n

    [foo]

    \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 = """
    [foo]: /url\n
    \n

    [foo]

    \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 = """

    Foo\n[bar]: /baz

    \n

    [bar]

    \n""" + assert md_to_html(md) == html + end + + fun test179 is test do + var md = """# [Foo]\n[foo]: /url\n> bar\n""" + var html = """

    Foo

    \n
    \n

    bar

    \n
    \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 = """

    foo,\nbar,\nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test181 is test do + var md = """[foo]\n\n> [foo]: /url\n""" + var html = """

    foo

    \n
    \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_links.nit b/lib/markdown2/tests/test_commonmark_links.nit new file mode 100644 index 0000000000..bedbbbd4bd --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_links.nit @@ -0,0 +1,514 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_links is test + +import test_markdown + +class TestCommonmarkLinks + super TestMarkdownHtml + test + + fun test462 is test do + var md = """[link](/uri "title")\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test463 is test do + var md = """[link](/uri)\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test464 is test do + var md = """[link]()\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test465 is test do + var md = """[link](<>)\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test466 is test do + var md = """[link](/my uri)\n""" + var html = """

    [link](/my uri)

    \n""" + assert md_to_html(md) == html + end + + fun test467 is test do + var md = """[link]()\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test468 is test do + var md = """[link](foo\nbar)\n""" + var html = """

    [link](foo\nbar)

    \n""" + assert md_to_html(md) == html + end + + fun test469 is test do + var md = """[link]()\n""" + var html = """

    [link]()

    \n""" + assert md_to_html(md) == html + end + + fun test470 is test do + var md = """[link](\\(foo\\))\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test471 is test do + var md = """[link](foo(and(bar)))\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test472 is test do + var md = """[link](foo\\(and\\(bar\\))\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test473 is test do + var md = """[link]()\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test474 is test do + var md = """[link](foo\\)\\:)\n""" + var html = """

    link

    \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 = """

    link

    \n

    link

    \n

    link

    \n""" + assert md_to_html(md) == html + end + + fun test476 is test do + var md = """[link](foo\\bar)\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test478 is test do + var md = """[link]("title")\n""" + var html = """

    link

    \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 = """

    link\nlink\nlink

    \n""" + assert md_to_html(md) == html + end + + fun test480 is test do + var md = """[link](/url "title \\""")\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test481 is test do + var md = """[link](/url "title")\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test482 is test do + var md = """[link](/url "title "and" title")\n""" + var html = """

    [link](/url "title "and" title")

    \n""" + assert md_to_html(md) == html + end + + fun test483 is test do + var md = """[link](/url 'title "and" title')\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test484 is test do + var md = """[link]( /uri\n "title" )\n""" + var html = """

    link

    \n""" + assert md_to_html(md) == html + end + + fun test485 is test do + var md = """[link] (/uri)\n""" + var html = """

    [link] (/uri)

    \n""" + assert md_to_html(md) == html + end + + fun test486 is test do + var md = """[link [foo [bar]]](/uri)\n""" + var html = """

    link [foo [bar]]

    \n""" + assert md_to_html(md) == html + end + + fun test487 is test do + var md = """[link] bar](/uri)\n""" + var html = """

    [link] bar](/uri)

    \n""" + assert md_to_html(md) == html + end + + fun test488 is test do + var md = """[link [bar](/uri)\n""" + var html = """

    [link bar

    \n""" + assert md_to_html(md) == html + end + + fun test489 is test do + var md = """[link \\[bar](/uri)\n""" + var html = """

    link [bar

    \n""" + assert md_to_html(md) == html + end + + fun test490 is test do + var md = """[link *foo **bar** `#`*](/uri)\n""" + var html = """

    link foo bar #

    \n""" + assert md_to_html(md) == html + end + + fun test491 is test do + var md = """[![moon](moon.jpg)](/uri)\n""" + var html = """

    moon

    \n""" + assert md_to_html(md) == html + end + + fun test492 is test do + var md = """[foo [bar](/uri)](/uri)\n""" + var html = """

    [foo bar](/uri)

    \n""" + assert md_to_html(md) == html + end + + fun test493 is test do + var md = """[foo *[bar [baz](/uri)](/uri)*](/uri)\n""" + var html = """

    [foo [bar baz](/uri)](/uri)

    \n""" + assert md_to_html(md) == html + end + + fun test494 is test do + var md = """![[[foo](uri1)](uri2)](uri3)\n""" + var html = """

    [foo](uri2)

    \n""" + assert md_to_html(md) == html + end + + fun test495 is test do + var md = """*[foo*](/uri)\n""" + var html = """

    *foo*

    \n""" + assert md_to_html(md) == html + end + + fun test496 is test do + var md = """[foo *bar](baz*)\n""" + var html = """

    foo *bar

    \n""" + assert md_to_html(md) == html + end + + fun test497 is test do + var md = """*foo [bar* baz]\n""" + var html = """

    foo [bar baz]

    \n""" + assert md_to_html(md) == html + end + + fun test498 is test do + var md = """[foo \n""" + var html = """

    [foo

    \n""" + assert md_to_html(md) == html + end + + fun test499 is test do + var md = """[foo`](/uri)`\n""" + var html = """

    [foo](/uri)

    \n""" + assert md_to_html(md) == html + end + + fun test500 is test do + var md = """[foo\n""" + var html = """

    [foohttp://example.com/?search=](uri)

    \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 = """

    foo

    \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 = """

    link [foo [bar]]

    \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 = """

    link [bar

    \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 = """

    link foo bar #

    \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 = """

    moon

    \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 = """

    [foo bar]ref

    \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 = """

    [foo bar baz]ref

    \n""" + assert md_to_html(md) == html + end + + fun test508 is test do + var md = """*[foo*][ref]\n\n[ref]: /uri\n""" + var html = """

    *foo*

    \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 = """

    foo *bar

    \n""" + assert md_to_html(md) == html + end + + fun test510 is test do + var md = """[foo \n\n[ref]: /uri\n""" + var html = """

    [foo

    \n""" + assert md_to_html(md) == html + end + + fun test511 is test do + var md = """[foo`][ref]`\n\n[ref]: /uri\n""" + var html = """

    [foo][ref]

    \n""" + assert md_to_html(md) == html + end + + fun test512 is test do + var md = """[foo\n\n[ref]: /uri\n""" + var html = """

    [foohttp://example.com/?search=][ref]

    \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 = """

    foo

    \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 = """

    Baz

    \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 = """

    [foo] bar

    \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 = """

    [foo]\nbar

    \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 = """

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test519 is test do + var md = """[bar][foo\\!]\n\n[foo!]: /url\n""" + var html = """

    [bar][foo!]

    \n""" + assert md_to_html(md) == html + end + + fun test520 is test do + var md = """[foo][ref[]\n\n[ref[]: /uri\n""" + var html = """

    [foo][ref[]

    \n

    [ref[]: /uri

    \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 = """

    [foo][ref[bar]]

    \n

    [ref[bar]]: /uri

    \n""" + assert md_to_html(md) == html + end + + fun test522 is test do + var md = """[[[foo]]]\n\n[[[foo]]]: /url\n""" + var html = """

    [[[foo]]]

    \n

    [[[foo]]]: /url

    \n""" + assert md_to_html(md) == html + end + + fun test523 is test do + var md = """[foo][ref\\[]\n\n[ref\\[]: /uri\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test524 is test do + var md = """[bar\\\\]: /uri\n\n[bar\\\\]\n""" + var html = """

    bar\\

    \n""" + assert md_to_html(md) == html + end + + fun test525 is test do + var md = """[]\n\n[]: /uri\n""" + var html = """

    []

    \n

    []: /uri

    \n""" + assert md_to_html(md) == html + end + + fun test526 is test do + var md = """[\n ]\n\n[\n ]: /uri\n""" + var html = """

    [\n]

    \n

    [\n]: /uri

    \n""" + assert md_to_html(md) == html + end + + fun test527 is test do + var md = """[foo][]\n\n[foo]: /url "title"\n""" + var html = """

    foo

    \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 = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test529 is test do + var md = """[Foo][]\n\n[foo]: /url "title"\n""" + var html = """

    Foo

    \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 = """

    foo\n[]

    \n""" + assert md_to_html(md) == html + end + + fun test531 is test do + var md = """[foo]\n\n[foo]: /url "title"\n""" + var html = """

    foo

    \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 = """

    foo bar

    \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 = """

    [foo bar]

    \n""" + assert md_to_html(md) == html + end + + fun test534 is test do + var md = """[[bar [foo]\n\n[foo]: /url\n""" + var html = """

    [[bar foo

    \n""" + assert md_to_html(md) == html + end + + fun test535 is test do + var md = """[Foo]\n\n[foo]: /url "title"\n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test536 is test do + var md = """[foo] bar\n\n[foo]: /url\n""" + var html = """

    foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test537 is test do + var md = """\\[foo]\n\n[foo]: /url "title"\n""" + var html = """

    [foo]

    \n""" + assert md_to_html(md) == html + end + + fun test538 is test do + var md = """[foo*]: /url\n\n*[foo*]\n""" + var html = """

    *foo*

    \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 = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test540 is test do + var md = """[foo][]\n\n[foo]: /url1\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test541 is test do + var md = """[foo]()\n\n[foo]: /url1\n""" + var html = """

    foo

    \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 = """

    foo(not a link)

    \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 = """

    [foo]bar

    \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 = """

    foobaz

    \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 = """

    [foo]bar

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_list_items.nit b/lib/markdown2/tests/test_commonmark_list_items.nit new file mode 100644 index 0000000000..b4896ede07 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_list_items.nit @@ -0,0 +1,310 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_list_items is test + +import test_markdown + +class TestCommonmarkListItems + super TestMarkdownHtml + test + + fun test216 is test do + var md = """A paragraph\nwith two lines.\n\n indented code\n\n> A block quote.\n""" + var html = """

    A paragraph\nwith two lines.

    \n
    indented code\n
    \n
    \n

    A block quote.

    \n
    \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 = """
      \n
    1. \n

      A paragraph\nwith two lines.

      \n
      indented code\n
      \n
      \n

      A block quote.

      \n
      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test218 is test do + var md = """- one\n\n two\n""" + var html = """
      \n
    • one
    • \n
    \n

    two

    \n""" + assert md_to_html(md) == html + end + + fun test219 is test do + var md = """- one\n\n two\n""" + var html = """
      \n
    • \n

      one

      \n

      two

      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test220 is test do + var md = """ - one\n\n two\n""" + var html = """
      \n
    • one
    • \n
    \n
     two\n
    \n""" + assert md_to_html(md) == html + end + + fun test221 is test do + var md = """ - one\n\n two\n""" + var html = """
      \n
    • \n

      one

      \n

      two

      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test222 is test do + var md = """ > > 1. one\n>>\n>> two\n""" + var html = """
    \n
    \n
      \n
    1. \n

      one

      \n

      two

      \n
    2. \n
    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test223 is test do + var md = """>>- one\n>>\n > > two\n""" + var html = """
    \n
    \n
      \n
    • one
    • \n
    \n

    two

    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test224 is test do + var md = """-one\n\n2.two\n""" + var html = """

    -one

    \n

    2.two

    \n""" + assert md_to_html(md) == html + end + + fun test225 is test do + var md = """- foo\n\n\n bar\n""" + var html = """
      \n
    • \n

      foo

      \n

      bar

      \n
    • \n
    \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 = """
      \n
    1. \n

      foo

      \n
      bar\n
      \n

      baz

      \n
      \n

      bam

      \n
      \n
    2. \n
    \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 = """
      \n
    • \n

      Foo

      \n
      bar\n\n\nbaz\n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test228 is test do + var md = """123456789. ok\n""" + var html = """
      \n
    1. ok
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test229 is test do + var md = """1234567890. not ok\n""" + var html = """

    1234567890. not ok

    \n""" + assert md_to_html(md) == html + end + + fun test230 is test do + var md = """0. ok\n""" + var html = """
      \n
    1. ok
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test231 is test do + var md = """003. ok\n""" + var html = """
      \n
    1. ok
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test232 is test do + var md = """-1. not ok\n""" + var html = """

    -1. not ok

    \n""" + assert md_to_html(md) == html + end + + fun test233 is test do + var md = """- foo\n\n bar\n""" + var html = """
      \n
    • \n

      foo

      \n
      bar\n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test234 is test do + var md = """ 10. foo\n\n bar\n""" + var html = """
      \n
    1. \n

      foo

      \n
      bar\n
      \n
    2. \n
    \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 = """
    indented code\n
    \n

    paragraph

    \n
    more code\n
    \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 = """
      \n
    1. \n
      indented code\n
      \n

      paragraph

      \n
      more code\n
      \n
    2. \n
    \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 = """
      \n
    1. \n
       indented code\n
      \n

      paragraph

      \n
      more code\n
      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test238 is test do + var md = """ foo\n\nbar\n""" + var html = """

    foo

    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test239 is test do + var md = """- foo\n\n bar\n""" + var html = """
      \n
    • foo
    • \n
    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test240 is test do + var md = """- foo\n\n bar\n""" + var html = """
      \n
    • \n

      foo

      \n

      bar

      \n
    • \n
    \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 = """
      \n
    • foo
    • \n
    • \n
      bar\n
      \n
    • \n
    • \n
      baz\n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test242 is test do + var md = """- \n foo\n""" + var html = """
      \n
    • foo
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test243 is test do + var md = """-\n\n foo\n""" + var html = """
      \n
    • \n
    \n

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test244 is test do + var md = """- foo\n-\n- bar\n""" + var html = """
      \n
    • foo
    • \n
    • \n
    • bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test245 is test do + var md = """- foo\n- \n- bar\n""" + var html = """
      \n
    • foo
    • \n
    • \n
    • bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test246 is test do + var md = """1. foo\n2.\n3. bar\n""" + var html = """
      \n
    1. foo
    2. \n
    3. \n
    4. bar
    5. \n
    \n""" + assert md_to_html(md) == html + end + + fun test247 is test do + var md = """*\n""" + var html = """
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test248 is test do + var md = """foo\n*\n\nfoo\n1.\n""" + var html = """

    foo\n*

    \n

    foo\n1.

    \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 = """
      \n
    1. \n

      A paragraph\nwith two lines.

      \n
      indented code\n
      \n
      \n

      A block quote.

      \n
      \n
    2. \n
    \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 = """
      \n
    1. \n

      A paragraph\nwith two lines.

      \n
      indented code\n
      \n
      \n

      A block quote.

      \n
      \n
    2. \n
    \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 = """
      \n
    1. \n

      A paragraph\nwith two lines.

      \n
      indented code\n
      \n
      \n

      A block quote.

      \n
      \n
    2. \n
    \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 = """
    1.  A paragraph\n    with two lines.\n\n        indented code\n\n    > A block quote.\n
    \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 = """
      \n
    1. \n

      A paragraph\nwith two lines.

      \n
      indented code\n
      \n
      \n

      A block quote.

      \n
      \n
    2. \n
    \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 = """
      \n
    1. A paragraph\nwith two lines.
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test255 is test do + var md = """> 1. > Blockquote\ncontinued here.\n""" + var html = """
    \n
      \n
    1. \n
      \n

      Blockquote\ncontinued here.

      \n
      \n
    2. \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test256 is test do + var md = """> 1. > Blockquote\n> continued here.\n""" + var html = """
    \n
      \n
    1. \n
      \n

      Blockquote\ncontinued here.

      \n
      \n
    2. \n
    \n
    \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 = """
      \n
    • foo\n
        \n
      • bar\n
          \n
        • baz\n
            \n
          • boo
          • \n
          \n
        • \n
        \n
      • \n
      \n
    • \n
    \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 = """
      \n
    • foo
    • \n
    • bar
    • \n
    • baz
    • \n
    • boo
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test259 is test do + var md = """10) foo\n - bar\n""" + var html = """
      \n
    1. foo\n
        \n
      • bar
      • \n
      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test260 is test do + var md = """10) foo\n - bar\n""" + var html = """
      \n
    1. foo
    2. \n
    \n
      \n
    • bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test261 is test do + var md = """- - foo\n""" + var html = """
      \n
    • \n
        \n
      • foo
      • \n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test262 is test do + var md = """1. - 2. foo\n""" + var html = """
      \n
    1. \n
        \n
      • \n
          \n
        1. foo
        2. \n
        \n
      • \n
      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test263 is test do + var md = """- # Foo\n- Bar\n ---\n baz\n""" + var html = """
      \n
    • \n

      Foo

      \n
    • \n
    • \n

      Bar

      \nbaz
    • \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_lists.nit b/lib/markdown2/tests/test_commonmark_lists.nit new file mode 100644 index 0000000000..05ef4d448c --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_lists.nit @@ -0,0 +1,166 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_lists is test + +import test_markdown + +class TestCommonmarkLists + super TestMarkdownHtml + test + + fun test264 is test do + var md = """- foo\n- bar\n+ baz\n""" + var html = """
      \n
    • foo
    • \n
    • bar
    • \n
    \n
      \n
    • baz
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test265 is test do + var md = """1. foo\n2. bar\n3) baz\n""" + var html = """
      \n
    1. foo
    2. \n
    3. bar
    4. \n
    \n
      \n
    1. baz
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test266 is test do + var md = """Foo\n- bar\n- baz\n""" + var html = """

    Foo

    \n
      \n
    • bar
    • \n
    • baz
    • \n
    \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 = """

    The number of windows in my house is\n14. The number of doors is 6.

    \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 = """

    The number of windows in my house is

    \n
      \n
    1. The number of doors is 6.
    2. \n
    \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 = """
      \n
    • \n

      foo

      \n
    • \n
    • \n

      bar

      \n
    • \n
    • \n

      baz

      \n
    • \n
    \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 = """
      \n
    • foo\n
        \n
      • bar\n
          \n
        • \n

          baz

          \n

          bim

          \n
        • \n
        \n
      • \n
      \n
    • \n
    \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 = """
      \n
    • foo
    • \n
    • bar
    • \n
    \n\n
      \n
    • baz
    • \n
    • bim
    • \n
    \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 = """
      \n
    • \n

      foo

      \n

      notcode

      \n
    • \n
    • \n

      foo

      \n
    • \n
    \n\n
    code\n
    \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 = """
      \n
    • a
    • \n
    • b
    • \n
    • c
    • \n
    • d
    • \n
    • e
    • \n
    • f
    • \n
    • g
    • \n
    \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 = """
      \n
    1. \n

      a

      \n
    2. \n
    3. \n

      b

      \n
    4. \n
    5. \n

      c

      \n
    6. \n
    \n""" + assert md_to_html(md) == html + end + + fun test277 is test do + var md = """- a\n- b\n\n- c\n""" + var html = """
      \n
    • \n

      a

      \n
    • \n
    • \n

      b

      \n
    • \n
    • \n

      c

      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test278 is test do + var md = """* a\n*\n\n* c\n""" + var html = """
      \n
    • \n

      a

      \n
    • \n
    • \n
    • \n

      c

      \n
    • \n
    \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 = """
      \n
    • \n

      a

      \n
    • \n
    • \n

      b

      \n

      c

      \n
    • \n
    • \n

      d

      \n
    • \n
    \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 = """
      \n
    • \n

      a

      \n
    • \n
    • \n

      b

      \n
    • \n
    • \n

      d

      \n
    • \n
    \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 = """
      \n
    • a
    • \n
    • \n
      b\n\n\n
      \n
    • \n
    • c
    • \n
    \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 = """
      \n
    • a\n
        \n
      • \n

        b

        \n

        c

        \n
      • \n
      \n
    • \n
    • d
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test283 is test do + var md = """* a\n > b\n >\n* c\n""" + var html = """
      \n
    • a\n
      \n

      b

      \n
      \n
    • \n
    • c
    • \n
    \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 = """
      \n
    • a\n
      \n

      b

      \n
      \n
      c\n
      \n
    • \n
    • d
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test285 is test do + var md = """- a\n""" + var html = """
      \n
    • a
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test286 is test do + var md = """- a\n - b\n""" + var html = """
      \n
    • a\n
        \n
      • b
      • \n
      \n
    • \n
    \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 = """
      \n
    1. \n
      foo\n
      \n

      bar

      \n
    2. \n
    \n""" + assert md_to_html(md) == html + end + + fun test288 is test do + var md = """* foo\n * bar\n\n baz\n""" + var html = """
      \n
    • \n

      foo

      \n
        \n
      • bar
      • \n
      \n

      baz

      \n
    • \n
    \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 = """
      \n
    • \n

      a

      \n
        \n
      • b
      • \n
      • c
      • \n
      \n
    • \n
    • \n

      d

      \n
        \n
      • e
      • \n
      • f
      • \n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_paragraphs.nit b/lib/markdown2/tests/test_commonmark_paragraphs.nit new file mode 100644 index 0000000000..6501c95c3a --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_paragraphs.nit @@ -0,0 +1,70 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_paragraphs is test + +import test_markdown + +class TestCommonmarkParagraphs + super TestMarkdownHtml + test + + fun test182 is test do + var md = """aaa\n\nbbb\n""" + var html = """

    aaa

    \n

    bbb

    \n""" + assert md_to_html(md) == html + end + + fun test183 is test do + var md = """aaa\nbbb\n\nccc\nddd\n""" + var html = """

    aaa\nbbb

    \n

    ccc\nddd

    \n""" + assert md_to_html(md) == html + end + + fun test184 is test do + var md = """aaa\n\n\nbbb\n""" + var html = """

    aaa

    \n

    bbb

    \n""" + assert md_to_html(md) == html + end + + fun test185 is test do + var md = """ aaa\n bbb\n""" + var html = """

    aaa\nbbb

    \n""" + assert md_to_html(md) == html + end + + fun test186 is test do + var md = """aaa\n bbb\n ccc\n""" + var html = """

    aaa\nbbb\nccc

    \n""" + assert md_to_html(md) == html + end + + fun test187 is test do + var md = """ aaa\nbbb\n""" + var html = """

    aaa\nbbb

    \n""" + assert md_to_html(md) == html + end + + fun test188 is test do + var md = """ aaa\nbbb\n""" + var html = """
    aaa\n
    \n

    bbb

    \n""" + assert md_to_html(md) == html + end + + fun test189 is test do + var md = """aaa \nbbb \n""" + var html = """

    aaa
    \nbbb

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_precedence.nit b/lib/markdown2/tests/test_commonmark_precedence.nit new file mode 100644 index 0000000000..a9938c0c8b --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_precedence.nit @@ -0,0 +1,28 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_precedence is test + +import test_markdown + +class TestCommonmarkPrecedence + super TestMarkdownHtml + test + + fun test12 is test do + var md = """- `one\n- two`\n""" + var html = """
      \n
    • `one
    • \n
    • two`
    • \n
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_raw_html.nit b/lib/markdown2/tests/test_commonmark_raw_html.nit new file mode 100644 index 0000000000..9e05a46211 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_raw_html.nit @@ -0,0 +1,148 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_raw_html is test + +import test_markdown + +class TestCommonmarkRawHTML + super TestMarkdownHtml + test + + fun test587 is test do + var md = """\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test588 is test do + var md = """\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test589 is test do + var md = """\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test590 is test do + var md = """\n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test591 is test do + var md = """Foo \n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test592 is test do + var md = """<33> <__>\n""" + var html = """

    <33> <__>

    \n""" + assert md_to_html(md) == html + end + + fun test593 is test do + var md = """
    \n""" + var html = """

    <a h*#ref="hi">

    \n""" + assert md_to_html(md) == html + end + + fun test594 is test do + var md = """
    <a href="hi'> <a href=hi'>

    \n""" + assert md_to_html(md) == html + end + + fun test595 is test do + var md = """< a><\nfoo>\n\n""" + var html = """

    < a><\nfoo><bar/ >\n<foo bar=baz\nbim!bop />

    \n""" + assert md_to_html(md) == html + end + + fun test596 is test do + var md = """
    \n""" + var html = """

    <a href='bar'title=title>

    \n""" + assert md_to_html(md) == html + end + + fun test597 is test do + var md = """
    \n""" + var html = """

    \n""" + assert md_to_html(md) == html + end + + fun test598 is test do + var md = """\n""" + var html = """

    </a href="foo">

    \n""" + assert md_to_html(md) == html + end + + fun test599 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test600 is test do + var md = """foo \n""" + var html = """

    foo <!-- not a comment -- two hyphens -->

    \n""" + assert md_to_html(md) == html + end + + fun test601 is test do + var md = """foo foo -->\n\nfoo \n""" + var html = """

    foo <!--> foo -->

    \n

    foo <!-- foo--->

    \n""" + assert md_to_html(md) == html + end + + fun test602 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test603 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test604 is test do + var md = """foo &<]]>\n""" + var html = """

    foo &<]]>

    \n""" + assert md_to_html(md) == html + end + + fun test605 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test606 is test do + var md = """foo \n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test607 is test do + var md = """\n""" + var html = """

    <a href=""">

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_setext_headings.nit b/lib/markdown2/tests/test_commonmark_setext_headings.nit new file mode 100644 index 0000000000..c92f4d00c7 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_setext_headings.nit @@ -0,0 +1,178 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_setext_headings is test + +import test_markdown + +class TestCommonmarkSetextHeadings + super TestMarkdownHtml + test + + fun test50 is test do + var md = """Foo *bar*\n=========\n\nFoo *bar*\n---------\n""" + var html = """

    Foo bar

    \n

    Foo bar

    \n""" + assert md_to_html(md) == html + end + + fun test51 is test do + var md = """Foo *bar\nbaz*\n====\n""" + var html = """

    Foo bar\nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test52 is test do + var md = """Foo\n-------------------------\n\nFoo\n=\n""" + var html = """

    Foo

    \n

    Foo

    \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 = """

    Foo

    \n

    Foo

    \n

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test54 is test do + var md = """ Foo\n ---\n\n Foo\n---\n""" + var html = """
    Foo\n---\n\nFoo\n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test55 is test do + var md = """Foo\n ---- \n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test56 is test do + var md = """Foo\n ---\n""" + var html = """

    Foo\n---

    \n""" + assert md_to_html(md) == html + end + + fun test57 is test do + var md = """Foo\n= =\n\nFoo\n--- -\n""" + var html = """

    Foo\n= =

    \n

    Foo

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test58 is test do + var md = """Foo \n-----\n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test59 is test do + var md = """Foo\\\n----\n""" + var html = """

    Foo\\

    \n""" + assert md_to_html(md) == html + end + + fun test60 is test do + var md = """`Foo\n----\n`\n\n
    \n""" + var html = """

    `Foo

    \n

    `

    \n

    <a title="a lot

    \n

    of dashes"/>

    \n""" + assert md_to_html(md) == html + end + + fun test61 is test do + var md = """> Foo\n---\n""" + var html = """
    \n

    Foo

    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test62 is test do + var md = """> foo\nbar\n===\n""" + var html = """
    \n

    foo\nbar\n===

    \n
    \n""" + assert md_to_html(md) == html + end + + fun test63 is test do + var md = """- Foo\n---\n""" + var html = """
      \n
    • Foo
    • \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test64 is test do + var md = """Foo\nBar\n---\n""" + var html = """

    Foo\nBar

    \n""" + assert md_to_html(md) == html + end + + fun test65 is test do + var md = """---\nFoo\n---\nBar\n---\nBaz\n""" + var html = """
    \n

    Foo

    \n

    Bar

    \n

    Baz

    \n""" + assert md_to_html(md) == html + end + + fun test66 is test do + var md = """\n====\n""" + var html = """

    ====

    \n""" + assert md_to_html(md) == html + end + + fun test67 is test do + var md = """---\n---\n""" + var html = """
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test68 is test do + var md = """- foo\n-----\n""" + var html = """
      \n
    • foo
    • \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test69 is test do + var md = """ foo\n---\n""" + var html = """
    foo\n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test70 is test do + var md = """> foo\n-----\n""" + var html = """
    \n

    foo

    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test71 is test do + var md = """\\> foo\n------\n""" + var html = """

    > foo

    \n""" + assert md_to_html(md) == html + end + + fun test72 is test do + var md = """Foo\n\nbar\n---\nbaz\n""" + var html = """

    Foo

    \n

    bar

    \n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test73 is test do + var md = """Foo\nbar\n\n---\n\nbaz\n""" + var html = """

    Foo\nbar

    \n
    \n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test74 is test do + var md = """Foo\nbar\n* * *\nbaz\n""" + var html = """

    Foo\nbar

    \n
    \n

    baz

    \n""" + assert md_to_html(md) == html + end + + fun test75 is test do + var md = """Foo\nbar\n\\---\nbaz\n""" + var html = """

    Foo\nbar\n---\nbaz

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_soft_line_breaks.nit b/lib/markdown2/tests/test_commonmark_soft_line_breaks.nit new file mode 100644 index 0000000000..c3ce0f1922 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_soft_line_breaks.nit @@ -0,0 +1,34 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_soft_line_breaks is test + +import test_markdown + +class TestCommonmarkSoftLineBreaks + super TestMarkdownHtml + test + + fun test623 is test do + var md = """foo\nbaz\n""" + var html = """

    foo\nbaz

    \n""" + assert md_to_html(md) == html + end + + fun test624 is test do + var md = """foo \n baz\n""" + var html = """

    foo\nbaz

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_tabs.nit b/lib/markdown2/tests/test_commonmark_tabs.nit new file mode 100644 index 0000000000..2a2f2ca039 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_tabs.nit @@ -0,0 +1,88 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_tabs is test + +import test_markdown + +class TestCommonmarkTabs + super TestMarkdownHtml + test + + fun test1 is test do + var md = """\tfoo\tbaz\t\tbim\n""" + var html = """
    foo\tbaz\t\tbim\n
    \n""" + assert md_to_html(md) == html + end + + fun test2 is test do + var md = """ \tfoo\tbaz\t\tbim\n""" + var html = """
    foo\tbaz\t\tbim\n
    \n""" + assert md_to_html(md) == html + end + + fun test3 is test do + var md = """ a\ta\n ὐ\ta\n""" + var html = """
    a\ta\nὐ\ta\n
    \n""" + assert md_to_html(md) == html + end + + fun test4 is test do + var md = """ - foo\n\n\tbar\n""" + var html = """
      \n
    • \n

      foo

      \n

      bar

      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test5 is test do + var md = """- foo\n\n\t\tbar\n""" + var html = """
      \n
    • \n

      foo

      \n
        bar\n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test6 is test do + var md = """>\t\tfoo\n""" + var html = """
    \n
      foo\n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test7 is test do + var md = """-\t\tfoo\n""" + var html = """
      \n
    • \n
        foo\n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test8 is test do + var md = """ foo\n\tbar\n""" + var html = """
    foo\nbar\n
    \n""" + assert md_to_html(md) == html + end + + fun test9 is test do + var md = """ - foo\n - bar\n\t - baz\n""" + var html = """
      \n
    • foo\n
        \n
      • bar\n
          \n
        • baz
        • \n
        \n
      • \n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test10 is test do + var md = """#\tFoo\n""" + var html = """

    Foo

    \n""" + assert md_to_html(md) == html + end + + fun test11 is test do + var md = """*\t*\t*\t\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_textual_content.nit b/lib/markdown2/tests/test_commonmark_textual_content.nit new file mode 100644 index 0000000000..235e20eb60 --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_textual_content.nit @@ -0,0 +1,40 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_textual_content is test + +import test_markdown + +class TestCommonmarkTextualContent + super TestMarkdownHtml + test + + fun test625 is test do + var md = """hello $.;'there\n""" + var html = """

    hello $.;'there

    \n""" + assert md_to_html(md) == html + end + + fun test626 is test do + var md = """Foo χρῆν\n""" + var html = """

    Foo χρῆν

    \n""" + assert md_to_html(md) == html + end + + fun test627 is test do + var md = """Multiple spaces\n""" + var html = """

    Multiple spaces

    \n""" + assert md_to_html(md) == html + end +end diff --git a/lib/markdown2/tests/test_commonmark_thematic_breaks.nit b/lib/markdown2/tests/test_commonmark_thematic_breaks.nit new file mode 100644 index 0000000000..ae68fa44ae --- /dev/null +++ b/lib/markdown2/tests/test_commonmark_thematic_breaks.nit @@ -0,0 +1,136 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_commonmark_thematic_breaks is test + +import test_markdown + +class TestCommonmarkThematicBreaks + super TestMarkdownHtml + test + + fun test13 is test do + var md = """***\n---\n___\n""" + var html = """
    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test14 is test do + var md = """+++\n""" + var html = """

    +++

    \n""" + assert md_to_html(md) == html + end + + fun test15 is test do + var md = """===\n""" + var html = """

    ===

    \n""" + assert md_to_html(md) == html + end + + fun test16 is test do + var md = """--\n**\n__\n""" + var html = """

    --\n**\n__

    \n""" + assert md_to_html(md) == html + end + + fun test17 is test do + var md = """ ***\n ***\n ***\n""" + var html = """
    \n
    \n
    \n""" + assert md_to_html(md) == html + end + + fun test18 is test do + var md = """ ***\n""" + var html = """
    ***\n
    \n""" + assert md_to_html(md) == html + end + + fun test19 is test do + var md = """Foo\n ***\n""" + var html = """

    Foo\n***

    \n""" + assert md_to_html(md) == html + end + + fun test20 is test do + var md = """_____________________________________\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test21 is test do + var md = """ - - -\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test22 is test do + var md = """ ** * ** * ** * **\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test23 is test do + var md = """- - - -\n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test24 is test do + var md = """- - - - \n""" + var html = """
    \n""" + assert md_to_html(md) == html + end + + fun test25 is test do + var md = """_ _ _ _ a\n\na------\n\n---a---\n""" + var html = """

    _ _ _ _ a

    \n

    a------

    \n

    ---a---

    \n""" + assert md_to_html(md) == html + end + + fun test26 is test do + var md = """ *-*\n""" + var html = """

    -

    \n""" + assert md_to_html(md) == html + end + + fun test27 is test do + var md = """- foo\n***\n- bar\n""" + var html = """
      \n
    • foo
    • \n
    \n
    \n
      \n
    • bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test28 is test do + var md = """Foo\n***\nbar\n""" + var html = """

    Foo

    \n
    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test29 is test do + var md = """Foo\n---\nbar\n""" + var html = """

    Foo

    \n

    bar

    \n""" + assert md_to_html(md) == html + end + + fun test30 is test do + var md = """* Foo\n* * *\n* Bar\n""" + var html = """
      \n
    • Foo
    • \n
    \n
    \n
      \n
    • Bar
    • \n
    \n""" + assert md_to_html(md) == html + end + + fun test31 is test do + var md = """- Foo\n- * * *\n""" + var html = """
      \n
    • Foo
    • \n
    • \n
      \n
    • \n
    \n""" + assert md_to_html(md) == html + end +end From fdf2203131835efdea7c72bbc6c72f397a4b5659 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:55:11 -0400 Subject: [PATCH 11/17] lib/markdown2: introduce markdown rendering to markdown Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_md_rendering.nit | 376 +++++++++++++ lib/markdown2/tests/test_markdown_md.nit | 663 +++++++++++++++++++++++ 2 files changed, 1039 insertions(+) create mode 100644 lib/markdown2/markdown_md_rendering.nit create mode 100644 lib/markdown2/tests/test_markdown_md.nit diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit new file mode 100644 index 0000000000..02439e1dcb --- /dev/null +++ b/lib/markdown2/markdown_md_rendering.nit @@ -0,0 +1,376 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown rendering of Markdown documents +module markdown_md_rendering + +import markdown_rendering + +# Markdown document renderer to Markdown +class MarkdownRenderer + super MdRenderer + + # Markdown output under construction + private var md: Buffer is noinit + + # Render `node` as Markdown + redef fun render(node) do + reset + enter_visit(node) + return md.write_to_string + end + + redef fun visit(node) do node.render_md(self) + + # Reset internal state + fun reset do + md = new Buffer + end + + # Current indentation level + private var indent = 0 + + # Are we currently in a blockquote? + var in_quote = 0 + + # Add a `md` string to the output + fun add_raw(md: String) do self.md.append(md) + + # Add a blank line to the output + fun add_line do add_raw "\n" + + # Add an indentation depending on `ident` level + fun add_indent do + add_raw " " * indent + end +end + +private class TextLengthVisitor + super MdVisitor + + var length = 0 + + redef fun visit(node) do node.process_len(self) +end + +redef class MdNode + + # Render `self` as Markdown + fun render_md(v: MarkdownRenderer) do visit_all(v) + + private fun process_len(v: TextLengthVisitor) do visit_all(v) +end + +redef class MdDocument + redef fun render_md(v) do + var node = first_child + while node != null do + v.enter_visit(node) + node = node.next + if node != null then + v.add_line + end + end + end +end + +# Blocks + +redef class MdBlockQuote + redef fun render_md(v) do + v.in_quote += 1 + var node = first_child + while node != null do + v.add_indent + v.add_raw "> " + v.enter_visit(node) + node = node.next + end + v.in_quote -= 1 + end +end + +redef class MdIndentedCodeBlock + redef fun render_md(v) do + var literal = self.literal + if literal == null then return + + var lines = literal.split("\n") + for i in [0..lines.length[ do + if i == lines.length - 1 then continue + var line = lines[i] + if line.is_empty then + v.add_raw "\n" + else + v.add_indent + if use_tabs then + v.add_raw "\t" + else + v.add_raw " " * 4 + end + v.add_raw line + v.add_line + end + end + end +end + +redef class MdFencedCodeBlock + redef fun render_md(v) do + var info = self.info + v.add_indent + v.add_raw fence_char.to_s * fence_length + v.add_raw info or else "" + for line in (literal or else "").split("\n") do + v.add_line + if not line.is_empty then + v.add_indent + end + v.add_raw line + end + v.add_indent + v.add_raw fence_char.to_s * fence_length + v.add_line + end +end + +redef class MdHeading + redef fun render_md(v) do + if is_setext then + visit_all(v) + var length_visitor = new TextLengthVisitor + length_visitor.enter_visit(self) + v.add_line + if level == 1 then + v.add_raw "=" * length_visitor.length + else + v.add_raw "-" * length_visitor.length + end + else + v.add_raw "#" * level + v.add_raw " " + visit_all(v) + if has_atx_trailing then + v.add_raw " " + v.add_raw "#" * level + end + end + v.add_line + end +end + +redef class MdOrderedList + # Children numbering + private var md_numbering: Int = start_number is lazy +end + +redef class MdListItem + redef fun render_md(v) do + var parent = self.parent + var is_tight = parent.as(MdListBlock).is_tight + + v.add_indent + if parent isa MdUnorderedList then + v.add_raw parent.bullet_marker.to_s + v.indent += 2 + else if parent isa MdOrderedList then + v.add_raw "{parent.md_numbering}{parent.delimiter.to_s}" + v.indent += 3 + end + + var node = first_child + if node != null then + v.add_raw " " + else + v.add_line + end + while node != null do + v.enter_visit(node) + node = node.next + if node != null and not is_tight then + v.add_line + end + end + + if next != null and not is_tight then + v.add_line + end + + if parent isa MdUnorderedList then + v.indent -= 2 + else if parent isa MdOrderedList then + parent.md_numbering += 1 + v.indent -= 3 + end + end +end + +redef class MdParagraph + redef fun render_md(v) do + if not parent isa MdBlockQuote and not parent isa MdListItem or prev != null then + v.add_indent + end + # if parent isa MdBlockQuote then + # v.add_raw "> " + # var node = first_child + # while node != null do + # v.enter_visit(node) + # if node isa MdSoftLineBreak or node isa MdHardLineBreak then + # v.add_raw "> " + # end + # node = node.next + # end + # v.add_line + # return + # end + visit_all(v) + v.add_line + end +end + +redef class MdThematicBreak + redef fun render_md(v) do + v.add_raw original_pattern + v.add_line + end +end + +redef class MdHtmlBlock + redef fun render_md(v) do + v.add_raw literal or else "" + v.add_line + end +end + +# Inlines + +redef class MdHardLineBreak + redef fun render_md(v) do + if has_backslash then + v.add_raw "\\" + else + v.add_raw " " + end + v.add_line + v.add_indent + v.add_raw "> " * v.in_quote + end + + redef fun process_len(v) do + super + v.length += 1 + end +end + +redef class MdSoftLineBreak + redef fun render_md(v) do + v.add_line + v.add_indent + v.add_raw "> " * v.in_quote + end + + redef fun process_len(v) do + super + v.length += 1 + end +end + +redef class MdCode + redef fun render_md(v) do + v.add_raw delimiter + v.add_raw literal + v.add_raw delimiter + end + + redef fun process_len(v) do + super + v.length += delimiter.length + end +end + +redef class MdDelimited + redef fun render_md(v) do + v.add_raw delimiter + visit_all(v) + v.add_raw delimiter + end + + redef fun process_len(v) do + super + v.length += delimiter.length * 2 + end +end + +redef class MdHtmlInline + redef fun render_md(v) do + v.add_raw literal + end + + redef fun process_len(v) do + v.length += literal.length + end +end + +redef class MdLinkOrImage + redef fun render_md(v) do + var title = self.title + v.add_raw "[" + visit_all(v) + v.add_raw "]" + v.add_raw "(" + if has_brackets then + v.add_raw "<" + end + v.add_raw destination + if has_brackets then + v.add_raw ">" + end + if title != null and not title.is_empty then + v.add_raw " \"" + v.add_raw title.replace("\"", "\\\"") + v.add_raw "\"" + end + v.add_raw ")" + end +end + + +redef class MdImage + redef fun render_md(v) do + v.add_raw "!" + super + end +end + +redef class MdLink + redef fun render_md(v) do + if is_autolink then + v.add_raw "<" + v.add_raw destination + v.add_raw ">" + return + end + super + end +end + +redef class MdText + redef fun render_md(v) do + v.add_raw literal + end + + redef fun process_len(v) do + v.length += literal.length + end +end diff --git a/lib/markdown2/tests/test_markdown_md.nit b/lib/markdown2/tests/test_markdown_md.nit new file mode 100644 index 0000000000..6705a2f21c --- /dev/null +++ b/lib/markdown2/tests/test_markdown_md.nit @@ -0,0 +1,663 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown rendering to markdown +module test_markdown_md is test + +import test_markdown +import markdown_md_rendering + +abstract class TestMarkdownMd + super TestMarkdown + + var md_renderer = new MarkdownRenderer + + fun md_to_md(md: String): String do + var doc = md_parser.parse(md) + doc.debug + return md_renderer.render(doc) + end +end + +class TestMdHeadings + super TestMarkdownMd + test + + fun test_no_trailings is test do + var md = """# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n""" + var exp = """# foo\n\n## foo\n\n### foo\n\n#### foo\n\n##### foo\n\n###### foo\n""" + assert md_to_md(md) == exp + end + + fun test_trailings is test do + var md = """# foo #\n## foo ##\n### foo ###\n#### foo ####\n##### foo #####\n""" + var exp = """# foo #\n\n## foo ##\n\n### foo ###\n\n#### foo ####\n\n##### foo #####\n""" + assert md_to_md(md) == exp + end + + fun test_setext is test do + var md = """Foo *bar*\n=========\nFoo *bar*\n---------\n""" + var exp = """Foo *bar*\n=========\n\nFoo *bar*\n---------\n""" + assert md_to_md(md) == exp + end +end + +class TestMdBlockQuotes + super TestMarkdownMd + test + + fun test191 is test do + var md = """> # Foo\n> bar\n> baz\n""" + assert md_to_md(md) == md + end + + fun test197 is test do + var md = """> foo\n---\n""" + var exp = """> foo\n\n---\n""" + assert md_to_md(md) == exp + end + + fun test198 is test do + var md = """> - foo\n- bar\n""" + var exp = """> - foo\n\n- bar\n""" + assert md_to_md(md) == exp + end + + fun test206 is test do + var md = """> foo\n> bar\n""" + assert md_to_md(md) == md + end + + fun test213 is test do + var md = """> > > foo\n> bar\n""" + var exp = """> > > foo\n> > > bar\n""" + assert md_to_md(md) == exp + end +end + +class TestMdLists + super TestMarkdownMd + test + + fun test264 is test do + var md = """- foo\n- bar\n+ baz\n""" + var exp = """- foo\n- bar\n\n+ baz\n""" + assert md_to_md(md) == exp + end + + fun test265 is test do + var md = """1. foo\n2. bar\n3) baz\n""" + var exp = """1. foo\n2. bar\n\n3) baz\n""" + assert md_to_md(md) == exp + end + + fun test270 is test do + var md = """- foo\n - bar\n - baz\n\n bim\n""" + assert md_to_md(md) == md + end + + fun test273 is test do + var md = """- a\n - b\n - c\n - d\n - e\n - f\n- g\n""" + var exp = """- a\n- b\n- c\n- d\n- e\n- f\n- g\n""" + assert md_to_md(md) == exp + end + + fun test274 is test do + var md = """1. a\n\n 2. b\n\n 3. c\n""" + var exp = """1. a\n\n2. b\n\n3. c\n""" + assert md_to_md(md) == exp + end + + fun test289 is test do + var md = """- a\n - b\n - c\n\n- d\n - e\n - f\n""" + var exp = """- a\n\n - b\n - c\n\n- d\n\n - e\n - f\n""" + assert md_to_md(md) == exp + end +end + +class TestMdkListItems + super TestMarkdownMd + test + + fun test217 is test do + var md = """1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n""" + var exp = """1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n""" + assert md_to_md(md) == exp + end + + fun test219 is test do + var md = """- one\n\n two\n""" + assert md_to_md(md) == md + end + + fun test221 is test do + var md = """ - one\n\n two\n""" + var exp = """- one\n\n two\n""" + assert md_to_md(md) == exp + end + + # FIXME + # fun test223 is test do + # var md = """>>- one\n>>\n > > two\n""" + # var exp = """> > - one\n> >\n> > two\n""" + # assert md_to_md(md) == exp + # end + + fun test225 is test do + var md = """- foo\n\n bar\n""" + assert md_to_md(md) == md + end + + fun test228 is test do + var md = """123456789. ok\n""" + assert md_to_md(md) == md + end + + fun test230 is test do + var md = """0. ok\n""" + assert md_to_md(md) == md + end + + fun test246 is test do + var md = """1. foo\n2.\n3. bar\n""" + assert md_to_md(md) == md + end + + fun test254 is test do + var md = """ 1. A paragraph\n with two lines.\n""" + var exp = """1. A paragraph\n with two lines.\n""" + assert md_to_md(md) == exp + end + + # FIXME + # fun test255 is test do + # var md = """> 1. > Blockquote\n> continued here.\n""" + # var exp = """> 1. > Blockquote\n > continued here.\n""" + # assert md_to_md(md) == exp + # end +end + +class TestMdFencedCodeBlocks + super TestMarkdownMd + test + + fun test88 is test do + var md = """```\nfoo\n```\n""" + assert md_to_md(md) == md + end + + fun test92 is test do + var md = """~~~\nfoo\n~~~\n""" + assert md_to_md(md) == md + end + + fun test111 is test do + var md = """```ruby\ndef foo(x)\n return 3\nend\n```\n""" + assert md_to_md(md) == md + end + + fun test112 is test do + var md = """~~~~~~\nSome markdown:\n~~~\n**hello**\n~~~\n~~~~~~\n""" + assert md_to_md(md) == md + end +end + +class TestMdIndentedCodeBlocks + super TestMarkdownMd + test + + fun test75 is test do + var md = """ a code block\n""" + assert md_to_md(md) == md + end + + fun test76 is test do + var md = """ a simple\n indented code block\n""" + assert md_to_md(md) == md + end + + fun test80 is test do + var md = """ chunk1\n\n chunk2\n \n \n \n chunk3\n""" + assert md_to_md(md) == """ chunk1\n\n chunk2\n\n\n\n chunk3\n""" + end + + fun test85 is test do + var md = """ foo\n bar\n""" + assert md_to_md(md) == md + end + + fun test87 is test do + var md = """\t\tfoo \n""" + assert md_to_md(md) == md + end +end + +class TestMdThematicBreaks + super TestMarkdownMd + test + + fun test13 is test do + var md = """***\n---\n___\n""" + var exp = """***\n\n---\n\n___\n""" + assert md_to_md(md) == exp + end + + fun test17 is test do + var md = """ ***\n ***\n ***\n""" + var exp = """***\n\n***\n\n***\n""" + assert md_to_md(md) == exp + end + + fun test20 is test do + var md = """_____________________________________\n""" + assert md_to_md(md) == md + end + + fun test21 is test do + var md = """ - - -\n""" + var exp = """- - -\n""" + assert md_to_md(md) == exp + end + + fun test22 is test do + var md = """ ** * ** * ** * **\n""" + var exp = """** * ** * ** * **\n""" + assert md_to_md(md) == exp + end + + fun test23 is test do + var md = """- - - -\n""" + assert md_to_md(md) == md + end +end + +class TestMdParagraphs + super TestMarkdownMd + test + + fun test182 is test do + var md = """aaa\n\nbbb\n""" + assert md_to_md(md) == md + end + + fun test183 is test do + var md = """aaa\nbbb\n\nccc\nddd\n""" + assert md_to_md(md) == md + end + + fun test186 is test do + var md = """aaa\n bbb\n ccc\n""" + var exp = """aaa\nbbb\nccc\n""" + assert md_to_md(md) == exp + end + + fun test187 is test do + var md = """ aaa\nbbb\n""" + var exp = """aaa\nbbb\n""" + assert md_to_md(md) == exp + end +end + +class TestMdHTMLBlocks + super TestMarkdownMd + test + + fun test116 is test do + var md = """
    \n
    \n*Hello*,\n\n_world_.\n
    \n\n
    \n""" + assert md_to_md(md) == md + end + + fun test119 is test do + var md = """
    \n*foo*\n""" + assert md_to_md(md) == md + end + + fun test120 is test do + var md = """
    \n\n*Markdown*\n\n
    \n""" + assert md_to_md(md) == md + end + + fun test121 is test do + var md = """
    \n
    \n""" + assert md_to_md(md) == md + end + + fun test124 is test do + var md = """
    \nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n\n""" + assert md_to_md(md) == md + end + + fun test138 is test do + var md = """\n""" + assert md_to_md(md) == md + end + + fun test139 is test do + var md = """\nh1 {color:red;}\n\np {color:blue;}\n\n""" + assert md_to_md(md) == md + end + + fun test149 is test do + var md = """\n""" + assert md_to_md(md) == md + end + + fun test150 is test do + var md = """ \n\n \n""" + assert md_to_md(md) == md + end +end + +# Inlines + +class TestMdLinks + super TestMarkdownMd + test + + fun test_autolink is test do + var md = """\n""" + assert md_to_md(md) == md + end + + fun test_automail is test do + var md = """\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]()\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]()\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 = """\n""" + assert md_to_md(md) == md + end + + fun test591 is test do + var md = """Foo \n""" + assert md_to_md(md) == md + end + + fun test596 is test do + var md = """\n""" + assert md_to_md(md) == md + end + + fun test599 is test do + var md = """foo \n""" + assert md_to_md(md) == md + end + + fun test601 is test do + var md = """foo foo -->\n\nfoo \n""" + assert md_to_md(md) == md + end + + fun test602 is test do + var md = """foo \n""" + assert md_to_md(md) == md + end + + fun test604 is test do + var md = """foo &<]]>\n""" + assert md_to_md(md) == md + end + + fun test606 is test do + var md = """foo \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 = """\n""" + assert md_to_md(md) == md + end + + fun test300 is test do + var md = """\n""" + assert md_to_md(md) == md + end +end From 4f37b72ded1564f1b549279d0c586805d48e1442 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:54:13 -0400 Subject: [PATCH 12/17] lib/markdown2: introduce markdown rendering to Manpages Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_man_rendering.nit | 232 +++++++++++++ lib/markdown2/tests/test_markdown_man.nit | 386 ++++++++++++++++++++++ 2 files changed, 618 insertions(+) create mode 100644 lib/markdown2/markdown_man_rendering.nit create mode 100644 lib/markdown2/tests/test_markdown_man.nit diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit new file mode 100644 index 0000000000..b2ee9ebd73 --- /dev/null +++ b/lib/markdown2/markdown_man_rendering.nit @@ -0,0 +1,232 @@ +# 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 + +# 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 diff --git a/lib/markdown2/tests/test_markdown_man.nit b/lib/markdown2/tests/test_markdown_man.nit new file mode 100644 index 0000000000..e64dd2ab68 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_man.nit @@ -0,0 +1,386 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown rendering to manpage +module test_markdown_man is test + +import test_markdown +import markdown_man_rendering + +# Abstract test class that defines the test methods for Man rendering +abstract class TestMarkdownMan + super TestMarkdown + + # Man renderer used in tests + var man_renderer = new ManRenderer + + # Render the `md` string as Manpage format + fun md_to_man(md: String): String do + var node = parse_md(md) + return man_renderer.render(node) + end +end + +class TestManRendering + super TestMarkdownMan + test + + fun test_headings is test do + var md = """# title1\n## title2\n### title3\n#### title4\n##### title5\n###### title6\n""" + var man = """.SH title1\n.SS title2\n.TP\ntitle3\n.TP\ntitle4\n.TP\ntitle5\n.TP\ntitle6\n""" + assert md_to_man(md) == man + end + + fun test_bquotes is test do + var md = """> line 1\n> line 2\n\n> line 3\n>line 4""" + var man = """.RS\nline 1\nline 2\n.RE\n.RS\nline 3\nline 4\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_breaks is test do + var md = """* * *""" + var man = """***\n""" + assert md_to_man(md) == man + end + + fun test_indented_code is test do + var md = """\tline 1\n\tline 2\n""" + var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_fenced_code is test do + var md = """~~~\nline 1\nline 2\n~~~\n""" + var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_escaped_code is test do + var md = """\tline - 1\n\tline - 2\n""" + var man = """.RS\n.nf\n\\f[C]\nline\\ \\-\\ 1\nline\\ \\-\\ 2\n\\f[]\n.fi\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_unordered_list is test do + var md = """* line 1\n* line 2\n""" + var man = """.RS\n.IP \\[bu] 3\nline 1\n.IP \\[bu] 3\nline 2\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_ordered_list is test do + var md = """2) line 1\n3) line 2\n""" + var man = """.RS\n.IP "2." 3\nline 1\n.IP "3." 3\nline 2\n.RE\n""" + assert md_to_man(md) == man + end + + fun test_paragraph is test do + var md = """line 1\nline 2\n\nline 3\nline 4\n""" + var man = """\nline 1\nline 2\n\nline 3\nline 4\n""" + assert md_to_man(md) == man + end + + fun test_escaped_text is test do + var md = """foo - bar\n""" + var man = """\nfoo \\- bar\n""" + assert md_to_man(md) == man + end + + fun test_inline_code is test do + var md = """`foo - bar`\n""" + var man = """\n\\f[C]foo\\ \\-\\ bar\\f[]\n""" + assert md_to_man(md) == man + end + + fun test_emphasis is test do + var md = """*foo*\n""" + var man = """\n\\f[I]foo\\f[]\n""" + assert md_to_man(md) == man + end + + fun test_strong_emphasis is test do + var md = """**foo**\n""" + var man = """\n\\f[B]foo\\f[]\n""" + assert md_to_man(md) == man + end + + fun test_link is test do + var md = """[foo](url "title")\n""" + var man = """\nfoo (url title)\n""" + assert md_to_man(md) == man + end + + fun test_image is test do + var md = """![foo](url "title")\n""" + var man = """\nfoo (url title)\n""" + assert md_to_man(md) == man + end + + fun test_full_document is test do + + var md = """ +# NAME + +nitdoc - generates HTML pages of API documentation from Nit source files. + +# SYNOPSIS + +nitdoc [*options*]... FILE... + +# DESCRIPTION + +`nitdoc` takes one or more modules and generate HTML pages of API documentation for these modules and their imported modules. + +The documentation is extracted from the comments found above the definition of modules, classes, and properties. + +Internally, `nitdoc` relies on the presence of the `dot` command from the [graphviz] project. +If the dot program is not present or not found, no image of hierarchies are generated. +See option `--no-dot`. + +The documentation of the Nit [standard library] is generated with this tool. + + [graphviz]: http://www.graphviz.org + [standard library]: http://nitlanguage.org/doc/stdlib + +# DOCUMENTATION FORMAT + +The format of the documentation is a dialect of [markdown] that allows GitHub fences (`~~~`). + +Code blocks are interpreted as snippets of Nit programs and intended to be used as examples of code. +When these code snippets are valid, executable and contain at least and `assert` clause, they could be automatically executed and verified. +See `nitunit(1)` for details. + + [markdown]: http://daringfireball.net/projects/markdown + +# OPTIONS + +### `-d`, `--dir` +Output directory. + +Where the HTML files are generated. + +By default, the directory is named `doc`. + +### `--source` +Format to link source code. + +The format string is used to generated links to some parts of the source-code. +Use `%f` for filename, `%l` for first line, and `%L` for last line. + +For instance, the [standard library] use the following value to link to files in GitHub: + + "https://github.com/nitlang/nit/blob/$(git rev-parse HEAD)/%f#L%l-%L" + +Here, the `git rev-parse HEAD` is used to link to the current snapshot revision of the file. + +### `--no-attributes` +Ignore the attributes. + +Note: In Nit, attributes are private. Therefore, this option is only useful +when combined with `--private`. + +### `--no-dot` +Do not generate graphs with graphviz. + +### `--private` +Also generate private API. + +## CUSTOMIZATION + +### `--share-dir` +Directory containing tools assets. + +By default `$NIT_DIR/share/nitdoc/` is used. + +### `--shareurl` +Use shareurl instead of copy shared files. + +By default, assets from the sharedir a copied into the output directory and referred with a relative path in the generated files. +With this option, the assets are not copied and the given URL of path is used in the generated files to locate assets. + +### `--custom-title` +Custom title for homepage. + +### `--custom-footer-text` +Custom footer text. + +### `--custom-overview-text` +Custom intro text for homepage. + +### `--custom-brand` +Custom link to external site. + +## SERVICES + +### `--github-upstream` +Git branch where edited commits will be pulled into (ex: user:repo:branch). + +### `--github-base-sha1` +Git sha1 of base commit used to create pull request. + +### `--github-gitdir` +Git working directory used to resolve path name (ex: /home/me/myproject/). + +### `--piwik-tracker` +Piwik tracker URL (ex: `nitlanguage.org/piwik/`). + +### `--piwik-site-id` +Piwik site ID. + +## TESTING + +### `--test` +Print test data (metrics and structure). + +### `--no-render` +Do not render HTML files. + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from +""" + + 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 From 7b813c0656391fec3614cdb7856ce225e39e4c3d Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 19:55:58 -0400 Subject: [PATCH 13/17] lib/markdown2: introduce markdown rendering to LaTeX Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_latex_rendering.nit | 404 +++++++++++++++ lib/markdown2/tests/test_markdown_latex.nit | 542 ++++++++++++++++++++ 2 files changed, 946 insertions(+) create mode 100644 lib/markdown2/markdown_latex_rendering.nit create mode 100644 lib/markdown2/tests/test_markdown_latex.nit diff --git a/lib/markdown2/markdown_latex_rendering.nit b/lib/markdown2/markdown_latex_rendering.nit new file mode 100644 index 0000000000..8cd5e33158 --- /dev/null +++ b/lib/markdown2/markdown_latex_rendering.nit @@ -0,0 +1,404 @@ +# 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 + +# 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 diff --git a/lib/markdown2/tests/test_markdown_latex.nit b/lib/markdown2/tests/test_markdown_latex.nit new file mode 100644 index 0000000000..ec856160ee --- /dev/null +++ b/lib/markdown2/tests/test_markdown_latex.nit @@ -0,0 +1,542 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown rendering to LaTeX +module test_markdown_latex is test + +import test_markdown +import markdown_latex_rendering + +# Abstract test class that defines the test methods for LaTeX rendering +abstract class TestMarkdownLatex + super TestMarkdown + + # LaTeX renderer used in tests + var tex_renderer = new LatexRenderer + + # Render the `md` string as LaTeX format + fun md_to_tex(md: String): String do + var node = parse_md(md) + return tex_renderer.render(node) + end +end + +class TestLatexRendering + super TestMarkdownLatex + test + + fun after_test is after do + tex_renderer.wrap_document = false + tex_renderer.use_listings = false + end + + fun test_document_wrapper is test do + var md = """ +This example needs a document wrapper. +""" + + var tex = """ +\\documentclass[letter,10pt]{article} + +\\usepackage[utf8]{inputenc} +\\usepackage{hyperref} +\\usepackage{graphicx} +\\usepackage{ulem} + +\\begin{document} + +This example needs a document wrapper. + +\\end{document} +""" + tex_renderer.wrap_document = true + assert md_to_tex(md) == tex + end + + fun test_atx_headings is test do + var md = """ +# Title 1 +## Title 2 +### Title 3 +#### Title 4 +##### Title 5 +###### Title 6 +""" + var tex = """ +\\section{Title 1} + +\\subsection{Title 2} + +\\subsubsection{Title 3} + +\\paragraph{Title 4} + +\\subparagraph{Title 5} + +\\textbf{Title 6} +""" + assert md_to_tex(md) == tex + end + + fun test_blockquotes is test do + var md = """ +> this is a +> multiline quote +""" + var tex = """ +\\begin{quote} + this is a + multiline quote +\\end{quote} +""" + assert md_to_tex(md) == tex + end + + fun test_thematic_breaks is test do + var md = """ +* * * +""" + var tex = """ +\\begin{center}\\rule{3in}{0.4pt}\\end{center} +""" + assert md_to_tex(md) == tex + end + + fun test_paragraphs is test do + var md = """ +a paragraph +on two lines + +another paragraph +""" + var tex = """ +a paragraph +on two lines + +another paragraph +""" + assert md_to_tex(md) == tex + end + + fun test_html_block is test do + var md = """ +

    + foo +

    + """ + var tex = """ +\\begin{verbatim} +

    + foo +

    +\\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 = """ + +""" + 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 From 3d4581e1e1ea33b861681f7eb667a023baf6f303 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 22:15:16 -0400 Subject: [PATCH 14/17] lib/markdown2: introduce Github extended mode Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_github.nit | 117 +++++++ lib/markdown2/markdown_html_rendering.nit | 19 ++ lib/markdown2/markdown_latex_rendering.nit | 19 ++ lib/markdown2/markdown_man_rendering.nit | 11 + lib/markdown2/markdown_md_rendering.nit | 1 + lib/markdown2/tests/test_markdown_github.nit | 313 +++++++++++++++++++ 6 files changed, 480 insertions(+) create mode 100644 lib/markdown2/markdown_github.nit create mode 100644 lib/markdown2/tests/test_markdown_github.nit diff --git a/lib/markdown2/markdown_github.nit b/lib/markdown2/markdown_github.nit new file mode 100644 index 0000000000..2239953cf3 --- /dev/null +++ b/lib/markdown2/markdown_github.nit @@ -0,0 +1,117 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown Github mode +# +# Enables: +# * strike processing: ~strike~ +# * super processing: ^super^ +# +# TODO table +# TODO todo lists +module markdown_github + +intrude import markdown_inline_parsing +intrude import markdown_block_parsing + +redef class MdParser + + # Enable Github mode + var github_mode = false is writable + + redef var inline_parser is lazy do + var parser = super + parser.github_mode = github_mode + return parser + end +end + +redef class MdInlineParser + + # Enable Github mode + private var github_mode = false + + redef var delimiter_processors is lazy do + var delimiters = super + if github_mode then + delimiters.add new MdStrikeProcessor + delimiters.add new MdSuperProcessor + end + return delimiters + end +end + +# Strike processor +class MdStrikeProcessor + super MdEmphasisDelimiterProcessor + noautoinit + + redef var delimiter_char = '~' + redef var min_length = 1 + redef fun delimiter_use(opener, closer) do return opener.original_length + + redef fun process(opener, closer, delimiter_use) do + var node = new MdStrike( + new MdLocation( + opener.location.line_start, + opener.location.column_start, + closer.location.line_end, + closer.location.column_end), + opening_delimiter.to_s * delimiter_use) + var tmp = opener.next + while tmp != null and tmp != closer do + var next = tmp.next + node.append_child(tmp) + tmp = next + end + opener.insert_after(node) + end +end + +# Striked text +class MdStrike + super MdDelimited +end + +# Super processor +class MdSuperProcessor + super MdEmphasisDelimiterProcessor + noautoinit + + redef var delimiter_char = '^' + redef var min_length = 1 + redef fun delimiter_use(opener, closer) do return opener.original_length + + redef fun process(opener, closer, delimiter_use) do + var node = new MdSuper( + new MdLocation( + opener.location.line_start, + opener.location.column_start, + closer.location.line_end, + closer.location.column_end), + opening_delimiter.to_s * delimiter_use) + var tmp = opener.next + while tmp != null and tmp != closer do + var next = tmp.next + node.append_child(tmp) + tmp = next + end + opener.insert_after(node) + end +end + +# Super text +class MdSuper + super MdDelimited +end diff --git a/lib/markdown2/markdown_html_rendering.nit b/lib/markdown2/markdown_html_rendering.nit index b8a88e35d0..e76fd2de02 100644 --- a/lib/markdown2/markdown_html_rendering.nit +++ b/lib/markdown2/markdown_html_rendering.nit @@ -16,6 +16,7 @@ module markdown_html_rendering import markdown_rendering +import markdown_github # Markdown document renderer to HTML class HtmlRenderer @@ -422,3 +423,21 @@ redef class MdText v.add_text literal end end + +# Github mode + +redef class MdStrike + redef fun render_html(v) do + v.add_raw "" + visit_all(v) + v.add_raw "" + end +end + +redef class MdSuper + redef fun render_html(v) do + v.add_raw "" + visit_all(v) + v.add_raw "" + end +end diff --git a/lib/markdown2/markdown_latex_rendering.nit b/lib/markdown2/markdown_latex_rendering.nit index 8cd5e33158..a19be2747b 100644 --- a/lib/markdown2/markdown_latex_rendering.nit +++ b/lib/markdown2/markdown_latex_rendering.nit @@ -16,6 +16,7 @@ module markdown_latex_rendering import markdown_rendering +import markdown_github # Markdown document renderer to LaTeX class LatexRenderer @@ -402,3 +403,21 @@ redef class MdText 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 diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit index b2ee9ebd73..14ed34b720 100644 --- a/lib/markdown2/markdown_man_rendering.nit +++ b/lib/markdown2/markdown_man_rendering.nit @@ -16,6 +16,7 @@ module markdown_man_rendering import markdown_rendering +import markdown_github # Markdown document renderer to Manpage class ManRenderer @@ -230,3 +231,13 @@ redef class MdText 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 diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit index 02439e1dcb..f8fe7ec441 100644 --- a/lib/markdown2/markdown_md_rendering.nit +++ b/lib/markdown2/markdown_md_rendering.nit @@ -16,6 +16,7 @@ module markdown_md_rendering import markdown_rendering +import markdown_github # Markdown document renderer to Markdown class MarkdownRenderer diff --git a/lib/markdown2/tests/test_markdown_github.nit b/lib/markdown2/tests/test_markdown_github.nit new file mode 100644 index 0000000000..04c04a413d --- /dev/null +++ b/lib/markdown2/tests/test_markdown_github.nit @@ -0,0 +1,313 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown Github mode +module test_markdown_github is test + +import test_markdown +import test_markdown_location +import test_markdown_md +import test_markdown_man +import test_markdown_latex + +redef class TestMarkdown + redef var md_parser is lazy do + var parser = super + parser.github_mode = true + return parser + end +end + +class TestGithubLocation + super TestMarkdownLocation + test + + fun test_github_strike is test do + var md = """ +A ~striked~ text. +""" + var loc = """ +MdDocument: 1,1--1,17 + MdParagraph: 1,1--1,17 + MdText: 1,1--1,2 + MdStrike: 1,3--1,11 + MdText: 1,4--1,10 + MdText: 1,12--1,17 +""" + assert md_to_loc(md) == loc + end + + fun test_github_strike2 is test do + var md = """ +A ~~striked~~ text. +""" + var loc = """ +MdDocument: 1,1--1,19 + MdParagraph: 1,1--1,19 + MdText: 1,1--1,2 + MdStrike: 1,3--1,13 + MdText: 1,5--1,11 + MdText: 1,14--1,19 +""" + assert md_to_loc(md) == loc + end + + fun test_github_super is test do + var md = """ +A ^supered^ text. +""" + var loc = """ +MdDocument: 1,1--1,17 + MdParagraph: 1,1--1,17 + MdText: 1,1--1,2 + MdSuper: 1,3--1,11 + MdText: 1,4--1,10 + MdText: 1,12--1,17 +""" + assert md_to_loc(md) == loc + end + + fun test_github_super2 is test do + var md = """ +A ^^supered^^ text. +""" + var loc = """ +MdDocument: 1,1--1,19 + MdParagraph: 1,1--1,19 + MdText: 1,1--1,2 + MdSuper: 1,3--1,13 + MdText: 1,5--1,11 + MdText: 1,14--1,19 +""" + assert md_to_loc(md) == loc + end +end + +class TestGithubHtml + super TestMarkdownHtml + test + + fun test_strike1 is test do + var md = """foo ~bar~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike2 is test do + var md = """foo ~~bar~~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike3 is test do + var md = """foo ~~~bar~~~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike4 is test do + var md = """foo ~~~~bar~~~~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike5 is test do + var md = """foo ~~~~~bar~~~~~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike6 is test do + var md = """foo ~~~~~~bar~~~~~~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad is test do + var md = """foo ~bar baz\n""" + var html = """

    foo ~bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad2 is test do + var md = """foo ~~bar~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad3 is test do + var md = """foo ~~~bar~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad4 is test do + var md = """foo ~~~~bar~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad5 is test do + var md = """foo ~~~~~bar~ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad6 is test do + var md = """foo bar~ baz\n""" + var html = """

    foo bar~ baz

    \n""" + assert md_to_html(md) == html + end + + fun test_strike_bad7 is test do + var md = """foo ~bar~~~~ baz\n""" + var html = """

    foo bar~~~ baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super1 is test do + var md = """foo ^bar^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super2 is test do + var md = """foo ^^bar^^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super3 is test do + var md = """foo ^^^bar^^^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super4 is test do + var md = """foo ^^^^bar^^^^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super5 is test do + var md = """foo ^^^^^bar^^^^^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super6 is test do + var md = """foo ^^^^^^bar^^^^^^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad1 is test do + var md = """foo ^bar baz\n""" + var html = """

    foo ^bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad is test do + var md = """foo ^^bar^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad3 is test do + var md = """foo ^^^bar^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad4 is test do + var md = """foo ^^^^bar^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad5 is test do + var md = """foo ^^^^^bar^ baz\n""" + var html = """

    foo bar baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad6 is test do + var md = """foo bar^ baz\n""" + var html = """

    foo bar^ baz

    \n""" + assert md_to_html(md) == html + end + + fun test_super_bad7 is test do + var md = """foo ^bar^^^^ baz\n""" + var html = """

    foo bar^^^ baz

    \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 From 4d6db18c69856f7ef6de634c437b1fd9b93c5898 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:02:24 -0400 Subject: [PATCH 15/17] lib/markdown2: introduce wikilinks parsing Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_html_rendering.nit | 15 ++ lib/markdown2/markdown_latex_rendering.nit | 15 ++ lib/markdown2/markdown_man_rendering.nit | 15 ++ lib/markdown2/markdown_md_rendering.nit | 15 ++ lib/markdown2/markdown_wikilinks.nit | 119 ++++++++++++ .../tests/test_markdown_wikilinks.nit | 180 ++++++++++++++++++ 6 files changed, 359 insertions(+) create mode 100644 lib/markdown2/markdown_wikilinks.nit create mode 100644 lib/markdown2/tests/test_markdown_wikilinks.nit diff --git a/lib/markdown2/markdown_html_rendering.nit b/lib/markdown2/markdown_html_rendering.nit index e76fd2de02..11addf61d1 100644 --- a/lib/markdown2/markdown_html_rendering.nit +++ b/lib/markdown2/markdown_html_rendering.nit @@ -17,6 +17,7 @@ module markdown_html_rendering import markdown_rendering import markdown_github +import markdown_wikilinks # Markdown document renderer to HTML class HtmlRenderer @@ -441,3 +442,17 @@ redef class MdSuper v.add_raw "" end end + +# Wikilinks mode + +redef class MdWikilink + + # Dummy rendering of wikilinks + # + # Clients should redefine this. + redef fun render_html(v) do + v.add_raw "" + visit_all(v) + v.add_raw "" + end +end diff --git a/lib/markdown2/markdown_latex_rendering.nit b/lib/markdown2/markdown_latex_rendering.nit index a19be2747b..b7c4abd836 100644 --- a/lib/markdown2/markdown_latex_rendering.nit +++ b/lib/markdown2/markdown_latex_rendering.nit @@ -17,6 +17,7 @@ module markdown_latex_rendering import markdown_rendering import markdown_github +import markdown_wikilinks # Markdown document renderer to LaTeX class LatexRenderer @@ -421,3 +422,17 @@ redef class MdSuper v.add_raw "\}" end end + +# Wikilinks + +redef class MdWikilink + redef fun render_latex(v) do + v.add_raw "\\texttt\{" + var title = self.title + if title != null then + v.add_text "{title} | " + end + v.add_text link + v.add_raw "\}" + end +end diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit index 14ed34b720..1e047d6c09 100644 --- a/lib/markdown2/markdown_man_rendering.nit +++ b/lib/markdown2/markdown_man_rendering.nit @@ -17,6 +17,7 @@ module markdown_man_rendering import markdown_rendering import markdown_github +import markdown_wikilinks # Markdown document renderer to Manpage class ManRenderer @@ -241,3 +242,17 @@ redef class MdStrike v.add "]" end end + +# Wikilinks + +redef class MdWikilink + redef fun render_man(v) do + v.add "(" + var title = self.title + if title != null then + v.add "{title} | " + end + v.add link + v.add ")" + end +end diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit index f8fe7ec441..eadda1b684 100644 --- a/lib/markdown2/markdown_md_rendering.nit +++ b/lib/markdown2/markdown_md_rendering.nit @@ -17,6 +17,7 @@ module markdown_md_rendering import markdown_rendering import markdown_github +import markdown_wikilinks # Markdown document renderer to Markdown class MarkdownRenderer @@ -375,3 +376,17 @@ redef class MdText v.length += literal.length end end + +# Wikilinks + +redef class MdWikilink + redef fun render_md(v) do + v.add_raw "[[" + var title = self.title + if title != null then + v.add_raw "{title} | " + end + v.add_raw link + v.add_raw "]]" + end +end diff --git a/lib/markdown2/markdown_wikilinks.nit b/lib/markdown2/markdown_wikilinks.nit new file mode 100644 index 0000000000..d6cc3a0ca1 --- /dev/null +++ b/lib/markdown2/markdown_wikilinks.nit @@ -0,0 +1,119 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown wikilinks processing +# +# Enables parsing of `[[wikilinks]]` syntax. +module markdown_wikilinks + +intrude import markdown_inline_parsing +intrude import markdown_block_parsing + +redef class MdParser + + # Enable wikilinks mode + var wikilinks_mode = false is writable + + redef var inline_parser is lazy do + var parser = super + parser.wikilinks_mode = wikilinks_mode + return parser + end +end + +redef class MdInlineParser + + # Enable wikilinks mode + private var wikilinks_mode = false + + redef fun parse_wikilink do + if not wikilinks_mode then return false + + # do we have two opening bracket? + var last_bracket = self.last_bracket + if last_bracket == null then return false + var first_bracket = last_bracket.prev + if first_bracket == null then return false + + # was the first bracket an image? + if first_bracket.is_image then return false + + # do we have two closing brackets? + if index >= input.length or input.chars[index] != ']' then return false + + advance 1 # skip last bracket + var start_index = first_bracket.index + 2 + var end_index = index - 2 + + # create wikilink node + var content = input.substring(start_index, end_index - start_index) + var parts = content.split("|") + var title = if parts.length > 1 then parts.first.trim else null + var link = parts.last.trim + + var wikilink = new MdWikilink( + new MdLocation( + first_bracket.node.location.line_start, + first_bracket.node.location.column_start - 1, + line, + column - 1), + link, title) + + var node = last_bracket.node.next + var in_link = false + while node != null do + var next = node.next + if not in_link then + if node isa MdText and node.literal.has("|") then + var buf = new Buffer + for c in node.literal.chars do + if c == '|' then + in_link = true + break + end + buf.add c + end + node.literal = buf.write_to_string.r_trim + end + wikilink.append_child(node) + else + node.unlink + end + node = next + end + + append_node(wikilink) + + # Process delimiters such as emphasis inside a link/image + process_delimiters(last_bracket.prev_delimiter) + merge_child_text_nodes(wikilink) + + # remove brackets + first_bracket.node.unlink + last_bracket.node.unlink + + return true + end +end + +# A Wikilink node +class MdWikilink + super MdNode + + # Wikilink link + var link: String is writable + + # Wikilink title + var title: nullable String = null is optional, writable +end diff --git a/lib/markdown2/tests/test_markdown_wikilinks.nit b/lib/markdown2/tests/test_markdown_wikilinks.nit new file mode 100644 index 0000000000..8721e142ee --- /dev/null +++ b/lib/markdown2/tests/test_markdown_wikilinks.nit @@ -0,0 +1,180 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown Wikilinks mode +module test_markdown_wikilinks is test + +import test_markdown +import test_markdown_location +import test_markdown_md +import test_markdown_man +import test_markdown_latex + +redef class TestMarkdown + redef var md_parser do + var parser = super + parser.wikilinks_mode = true + return parser + end +end + +class TestWikilinksLocation + super TestMarkdownLocation + test + + fun test_wikilinks1 is test do + var md = """ +A [[wiki link]] and text. +""" + var loc = """ +MdDocument: 1,1--1,25 + MdParagraph: 1,1--1,25 + MdText: 1,1--1,2 + MdWikilink: 1,3--1,15 + MdText: 1,5--1,13 + MdText: 1,16--1,25 +""" + assert md_to_loc(md) == loc + end + + fun test_wikilinks2 is test do + var md = """ +A [[wiki: link | with: more, args: end]] and text. +""" + var loc = """ +MdDocument: 1,1--1,50 + MdParagraph: 1,1--1,50 + MdText: 1,1--1,2 + MdWikilink: 1,3--1,40 + MdText: 1,5--1,38 + MdText: 1,41--1,50 +""" + assert md_to_loc(md) == loc + end + +end + +class TestWikilinksHtml + super TestMarkdownHtml + test + + fun test_wikilinks1 is test do + var md = """[[foo]]\n""" + var html = """

    foo

    \n""" + assert md_to_html(md) == html + end + + fun test_wikilinks2 is test do + var md = """[[foo | bar baz]]\n""" + var html = """

    foo

    \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 = """

    This is a link and this is another one.

    \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 = """

    very: complex

    \n""" + assert md_to_html(md) == html + end + + fun test_wikilink_bad1 is test do + var md = """Not a [wikilink]].\n""" + var html = """

    Not a [wikilink]].

    \n""" + assert md_to_html(md) == html + end + + fun test_wikilink_bad2 is test do + var md = """Not a [[wikilink].\n""" + var html = """

    Not a [[wikilink].

    \n""" + assert md_to_html(md) == html + end + + fun test_wikilink_bad3 is test do + var md = """Not a ![[wikilink]].\n""" + var html = """

    Not a ![[wikilink]].

    \n""" + assert md_to_html(md) == html + end + + fun test_wikilink_bad4 is test do + var md = """Not a [wikilink].\n""" + var html = """

    Not a [wikilink].

    \n""" + assert md_to_html(md) == html + end + + fun test_link is test do + var md = """A standard [link](url).\n""" + var html = """

    A standard link.

    \n""" + assert md_to_html(md) == html + end + + fun test_image is test do + var md = """A standard ![image](url).\n""" + var html = """

    A standard image.

    \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 = """

    A standard link definition.

    \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 = """

    wikilinks are not

    \n

    link definition: url

    \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 From 8a3b5802fd9f5c4f47ff89fb6ce0934b963006fd Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 22:38:52 -0400 Subject: [PATCH 16/17] lib/markdown2: introduce `nitmd` client Signed-off-by: Alexandre Terrasa --- lib/markdown2/nitmd.nit | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 lib/markdown2/nitmd.nit diff --git a/lib/markdown2/nitmd.nit b/lib/markdown2/nitmd.nit new file mode 100644 index 0000000000..5a6970f411 --- /dev/null +++ b/lib/markdown2/nitmd.nit @@ -0,0 +1,73 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A Markdown parser for Nit. +module nitmd + +import markdown_html_rendering +import markdown_md_rendering +import markdown_man_rendering +import markdown_latex_rendering + +import config + +var opt_to = new OptionString("Specify output format (html, md, man, latex)", "-t", "--to") + +var usage = new Buffer +usage.append "Usage: nitmd [-t format] [file.md]\n" +usage.append "Translate Markdown documents to other formats.\n\n" +usage.append "If no argument, read the Markdown input from `stdin`." + +var config = new Config +config.add_option(opt_to) +config.tool_description = usage.write_to_string + +config.parse_options(args) +if config.args.length > 1 then + config.usage + exit 1 +end + +var md +if config.args.is_empty then + md = sys.stdin.read_all +else + var file = config.args.first + if not file.file_exists then + print "'{file}' not found" + exit 1 + end + md = file.to_path.read_all +end + +# Parse the input +var parser = new MdParser +var node = parser.parse(md) + +var renderer: MdRenderer +var to = opt_to.value +if to == null or to == "html" then + renderer = new HtmlRenderer +else if to == "md" then + renderer = new MarkdownRenderer +else if to == "man" then + renderer = new ManRenderer +else if to == "latex" then + renderer = new LatexRenderer +else + print "Unknown output format: {to}" + exit 1 + return +end +printn renderer.render(node) From 278b28992e380f283c3984f0b702278ee94b445e Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Wed, 20 Jun 2018 11:15:15 -0400 Subject: [PATCH 17/17] benchmarks/markdown: add benches for markdown2 Signed-off-by: Alexandre Terrasa --- benchmarks/markdown/bench_markdown.sh | 24 ++++++++++++++ benchmarks/markdown/engines/Makefile | 6 +++- benchmarks/markdown/engines/nitmd2/Makefile | 32 +++++++++++++++++++ benchmarks/markdown/engines/nitmd2/nitmd2.nit | 26 +++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 benchmarks/markdown/engines/nitmd2/Makefile create mode 100644 benchmarks/markdown/engines/nitmd2/nitmd2.nit diff --git a/benchmarks/markdown/bench_markdown.sh b/benchmarks/markdown/bench_markdown.sh index d6826e1f6d..338ba145ac 100755 --- a/benchmarks/markdown/bench_markdown.sh +++ b/benchmarks/markdown/bench_markdown.sh @@ -97,6 +97,30 @@ function bench_nitmd-o() } bench_nitmd-o +function bench_nitmd2() +{ + name="$FUNCNAME" + skip_test "$name" && return + prepare_res $outdir/nitmd2.dat "nitmd2" "nitmd2" + for file in $bncdir/*.md; do + bench=`basename $file .md` + bench_command "$bench" "" "$engdir/nitmd2/nitmd2" "$file" "$s" + done +} +bench_nitmd2 + +function bench_nitmd2-o() +{ + name="$FUNCNAME" + skip_test "$name" && return + prepare_res $outdir/nitmd2-o.dat "nitmd2-o" "nitmd2-o" + for file in $bncdir/*.md; do + bench=`basename $file .md` + bench_command "$bench" "" "$engdir/nitmd2/nitmd2-o" "$file" "$s" + done +} +bench_nitmd2-o + function bench_txtmark() { name="$FUNCNAME" diff --git a/benchmarks/markdown/engines/Makefile b/benchmarks/markdown/engines/Makefile index 1eeda8872e..711d69f62d 100644 --- a/benchmarks/markdown/engines/Makefile +++ b/benchmarks/markdown/engines/Makefile @@ -14,11 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -all: nitmd/nitmd txtmark/Txtmark.class markdown4j/Markdown4j.class +all: nitmd/nitmd nitmd2/nitmd2 txtmark/Txtmark.class markdown4j/Markdown4j.class nitmd/nitmd: make -C nitmd +nitmd2/nitmd2: + make -C nitmd2 + txtmark/Txtmark.class: make -C txtmark @@ -30,6 +33,7 @@ pandoc/pandoc: clean: make -C nitmd clean + make -C nitmd2 clean make -C txtmark clean make -C markdown4j clean make -C pandoc clean diff --git a/benchmarks/markdown/engines/nitmd2/Makefile b/benchmarks/markdown/engines/nitmd2/Makefile new file mode 100644 index 0000000000..526ca83174 --- /dev/null +++ b/benchmarks/markdown/engines/nitmd2/Makefile @@ -0,0 +1,32 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2015 Alexandre Terrasa +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +NITC=../../../../bin/nitc + +all: nitmd2 nitmd2-o + +nitmd2: + $(NITC) nitmd2.nit + +nitmd2-o: + $(NITC) --semi-global nitmd2.nit -o $@ + +test: all + ./nitmd2 ../../benches/hello.md 5 + ./nitmd2-o ../../benches/hello.md 5 + +clean: + rm -rf nitmd2 nitmd2-o diff --git a/benchmarks/markdown/engines/nitmd2/nitmd2.nit b/benchmarks/markdown/engines/nitmd2/nitmd2.nit new file mode 100644 index 0000000000..d7056181a3 --- /dev/null +++ b/benchmarks/markdown/engines/nitmd2/nitmd2.nit @@ -0,0 +1,26 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import markdown2 + +var file = args.first +var n = args[1].to_i + +var str = file.to_path.read_all +var parser = new MdParser +var renderer = new HtmlRenderer +for i in [1..n] do + var doc = parser.parse(str) + print renderer.render(doc) +end