Merge pull request #29 from athom/master add EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK flag to make it closer to GFM
Vytautas Šaltenis Vytautas.Shaltenis@gmail.com
Sat, 10 Aug 2013 13:13:13 -0700
9 files changed,
403 insertions(+),
18 deletions(-)
M
block.go
→
block.go
@@ -1230,6 +1230,17 @@ p.renderParagraph(out, data[:i])
return i } + // if there's a list after this, paragraph is over + if p.flags&EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK != 0 { + if p.uliPrefix(current) != 0 || + p.oliPrefix(current) != 0 || + p.quotePrefix(current) != 0 || + p.codePrefix(current) != 0 { + p.renderParagraph(out, data[:i]) + return i + } + } + // otherwise, scan to the beginning of the next line for data[i] != '\n' { i++
M
block_test.go
→
block_test.go
@@ -692,3 +692,281 @@ "<table>\n<thead>\n<tr>\n<td>a</td>\n<td>b|c</td>\n<td>d</td>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td>f</td>\n<td>g|h</td>\n<td>i</td>\n</tr>\n</tbody>\n</table>\n",
} doTestsBlock(t, tests, EXTENSION_TABLES) } + +func TestUnorderedListWith_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { + var tests = []string{ + "* Hello\n", + "<ul>\n<li>Hello</li>\n</ul>\n", + + "* Yin\n* Yang\n", + "<ul>\n<li>Yin</li>\n<li>Yang</li>\n</ul>\n", + + "* Ting\n* Bong\n* Goo\n", + "<ul>\n<li>Ting</li>\n<li>Bong</li>\n<li>Goo</li>\n</ul>\n", + + "* Yin\n\n* Yang\n", + "<ul>\n<li><p>Yin</p></li>\n\n<li><p>Yang</p></li>\n</ul>\n", + + "* Ting\n\n* Bong\n* Goo\n", + "<ul>\n<li><p>Ting</p></li>\n\n<li><p>Bong</p></li>\n\n<li><p>Goo</p></li>\n</ul>\n", + + "+ Hello\n", + "<ul>\n<li>Hello</li>\n</ul>\n", + + "+ Yin\n+ Yang\n", + "<ul>\n<li>Yin</li>\n<li>Yang</li>\n</ul>\n", + + "+ Ting\n+ Bong\n+ Goo\n", + "<ul>\n<li>Ting</li>\n<li>Bong</li>\n<li>Goo</li>\n</ul>\n", + + "+ Yin\n\n+ Yang\n", + "<ul>\n<li><p>Yin</p></li>\n\n<li><p>Yang</p></li>\n</ul>\n", + + "+ Ting\n\n+ Bong\n+ Goo\n", + "<ul>\n<li><p>Ting</p></li>\n\n<li><p>Bong</p></li>\n\n<li><p>Goo</p></li>\n</ul>\n", + + "- Hello\n", + "<ul>\n<li>Hello</li>\n</ul>\n", + + "- Yin\n- Yang\n", + "<ul>\n<li>Yin</li>\n<li>Yang</li>\n</ul>\n", + + "- Ting\n- Bong\n- Goo\n", + "<ul>\n<li>Ting</li>\n<li>Bong</li>\n<li>Goo</li>\n</ul>\n", + + "- Yin\n\n- Yang\n", + "<ul>\n<li><p>Yin</p></li>\n\n<li><p>Yang</p></li>\n</ul>\n", + + "- Ting\n\n- Bong\n- Goo\n", + "<ul>\n<li><p>Ting</p></li>\n\n<li><p>Bong</p></li>\n\n<li><p>Goo</p></li>\n</ul>\n", + + "*Hello\n", + "<p>*Hello</p>\n", + + "* Hello \n", + "<ul>\n<li>Hello</li>\n</ul>\n", + + "* Hello \n Next line \n", + "<ul>\n<li>Hello\nNext line</li>\n</ul>\n", + + "Paragraph\n* No linebreak\n", + "<p>Paragraph</p>\n\n<ul>\n<li>No linebreak</li>\n</ul>\n", + + "Paragraph\n\n* Linebreak\n", + "<p>Paragraph</p>\n\n<ul>\n<li>Linebreak</li>\n</ul>\n", + + "* List\n * Nested list\n", + "<ul>\n<li>List\n\n<ul>\n<li>Nested list</li>\n</ul></li>\n</ul>\n", + + "* List\n\n * Nested list\n", + "<ul>\n<li><p>List</p>\n\n<ul>\n<li>Nested list</li>\n</ul></li>\n</ul>\n", + + "* List\n Second line\n\n + Nested\n", + "<ul>\n<li><p>List\nSecond line</p>\n\n<ul>\n<li>Nested</li>\n</ul></li>\n</ul>\n", + + "* List\n + Nested\n\n Continued\n", + "<ul>\n<li><p>List</p>\n\n<ul>\n<li>Nested</li>\n</ul>\n\n<p>Continued</p></li>\n</ul>\n", + + "* List\n * shallow indent\n", + "<ul>\n<li>List\n\n<ul>\n<li>shallow indent</li>\n</ul></li>\n</ul>\n", + + "* List\n" + + " * shallow indent\n" + + " * part of second list\n" + + " * still second\n" + + " * almost there\n" + + " * third level\n", + "<ul>\n" + + "<li>List\n\n" + + "<ul>\n" + + "<li>shallow indent</li>\n" + + "<li>part of second list</li>\n" + + "<li>still second</li>\n" + + "<li>almost there\n\n" + + "<ul>\n" + + "<li>third level</li>\n" + + "</ul></li>\n" + + "</ul></li>\n" + + "</ul>\n", + + "* List\n extra indent, same paragraph\n", + "<ul>\n<li>List\n extra indent, same paragraph</li>\n</ul>\n", + + "* List\n\n code block\n", + "<ul>\n<li><p>List</p>\n\n<pre><code>code block\n</code></pre></li>\n</ul>\n", + + "* List\n\n code block with spaces\n", + "<ul>\n<li><p>List</p>\n\n<pre><code> code block with spaces\n</code></pre></li>\n</ul>\n", + + "* List\n\n * sublist\n\n normal text\n\n * another sublist\n", + "<ul>\n<li><p>List</p>\n\n<ul>\n<li>sublist</li>\n</ul>\n\n<p>normal text</p>\n\n<ul>\n<li>another sublist</li>\n</ul></li>\n</ul>\n", + } + doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) +} + +func TestOrderedList_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { + var tests = []string{ + "1. Hello\n", + "<ol>\n<li>Hello</li>\n</ol>\n", + + "1. Yin\n2. Yang\n", + "<ol>\n<li>Yin</li>\n<li>Yang</li>\n</ol>\n", + + "1. Ting\n2. Bong\n3. Goo\n", + "<ol>\n<li>Ting</li>\n<li>Bong</li>\n<li>Goo</li>\n</ol>\n", + + "1. Yin\n\n2. Yang\n", + "<ol>\n<li><p>Yin</p></li>\n\n<li><p>Yang</p></li>\n</ol>\n", + + "1. Ting\n\n2. Bong\n3. Goo\n", + "<ol>\n<li><p>Ting</p></li>\n\n<li><p>Bong</p></li>\n\n<li><p>Goo</p></li>\n</ol>\n", + + "1 Hello\n", + "<p>1 Hello</p>\n", + + "1.Hello\n", + "<p>1.Hello</p>\n", + + "1. Hello \n", + "<ol>\n<li>Hello</li>\n</ol>\n", + + "1. Hello \n Next line \n", + "<ol>\n<li>Hello\nNext line</li>\n</ol>\n", + + "Paragraph\n1. No linebreak\n", + "<p>Paragraph</p>\n\n<ol>\n<li>No linebreak</li>\n</ol>\n", + + "Paragraph\n\n1. Linebreak\n", + "<p>Paragraph</p>\n\n<ol>\n<li>Linebreak</li>\n</ol>\n", + + "1. List\n 1. Nested list\n", + "<ol>\n<li>List\n\n<ol>\n<li>Nested list</li>\n</ol></li>\n</ol>\n", + + "1. List\n\n 1. Nested list\n", + "<ol>\n<li><p>List</p>\n\n<ol>\n<li>Nested list</li>\n</ol></li>\n</ol>\n", + + "1. List\n Second line\n\n 1. Nested\n", + "<ol>\n<li><p>List\nSecond line</p>\n\n<ol>\n<li>Nested</li>\n</ol></li>\n</ol>\n", + + "1. List\n 1. Nested\n\n Continued\n", + "<ol>\n<li><p>List</p>\n\n<ol>\n<li>Nested</li>\n</ol>\n\n<p>Continued</p></li>\n</ol>\n", + + "1. List\n 1. shallow indent\n", + "<ol>\n<li>List\n\n<ol>\n<li>shallow indent</li>\n</ol></li>\n</ol>\n", + + "1. List\n" + + " 1. shallow indent\n" + + " 2. part of second list\n" + + " 3. still second\n" + + " 4. almost there\n" + + " 1. third level\n", + "<ol>\n" + + "<li>List\n\n" + + "<ol>\n" + + "<li>shallow indent</li>\n" + + "<li>part of second list</li>\n" + + "<li>still second</li>\n" + + "<li>almost there\n\n" + + "<ol>\n" + + "<li>third level</li>\n" + + "</ol></li>\n" + + "</ol></li>\n" + + "</ol>\n", + + "1. List\n extra indent, same paragraph\n", + "<ol>\n<li>List\n extra indent, same paragraph</li>\n</ol>\n", + + "1. List\n\n code block\n", + "<ol>\n<li><p>List</p>\n\n<pre><code>code block\n</code></pre></li>\n</ol>\n", + + "1. List\n\n code block with spaces\n", + "<ol>\n<li><p>List</p>\n\n<pre><code> code block with spaces\n</code></pre></li>\n</ol>\n", + + "1. List\n * Mixted list\n", + "<ol>\n<li>List\n\n<ul>\n<li>Mixted list</li>\n</ul></li>\n</ol>\n", + + "1. List\n * Mixed list\n", + "<ol>\n<li>List\n\n<ul>\n<li>Mixed list</li>\n</ul></li>\n</ol>\n", + + "* Start with unordered\n 1. Ordered\n", + "<ul>\n<li>Start with unordered\n\n<ol>\n<li>Ordered</li>\n</ol></li>\n</ul>\n", + + "* Start with unordered\n 1. Ordered\n", + "<ul>\n<li>Start with unordered\n\n<ol>\n<li>Ordered</li>\n</ol></li>\n</ul>\n", + + "1. numbers\n1. are ignored\n", + "<ol>\n<li>numbers</li>\n<li>are ignored</li>\n</ol>\n", + } + doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) +} + +func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { + var tests = []string{ + "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", + "<pre><code class=\"go\">func foo() bool {\n return true;\n}\n</code></pre>\n", + + "``` c\n/* special & char < > \" escaping */\n```\n", + "<pre><code class=\"c\">/* special & char < > " escaping */\n</code></pre>\n", + + "``` c\nno *inline* processing ~~of text~~\n```\n", + "<pre><code class=\"c\">no *inline* processing ~~of text~~\n</code></pre>\n", + + "```\nNo language\n```\n", + "<pre><code>No language\n</code></pre>\n", + + "``` {ocaml}\nlanguage in braces\n```\n", + "<pre><code class=\"ocaml\">language in braces\n</code></pre>\n", + + "``` {ocaml} \nwith extra whitespace\n```\n", + "<pre><code class=\"ocaml\">with extra whitespace\n</code></pre>\n", + + "```{ ocaml }\nwith extra whitespace\n```\n", + "<pre><code class=\"ocaml\">with extra whitespace\n</code></pre>\n", + + "~ ~~ java\nWith whitespace\n~~~\n", + "<p>~ ~~ java\nWith whitespace\n~~~</p>\n", + + "~~\nonly two\n~~\n", + "<p>~~\nonly two\n~~</p>\n", + + "```` python\nextra\n````\n", + "<pre><code class=\"python\">extra\n</code></pre>\n", + + "~~~ perl\nthree to start, four to end\n~~~~\n", + "<p>~~~ perl\nthree to start, four to end\n~~~~</p>\n", + + "~~~~ perl\nfour to start, three to end\n~~~\n", + "<p>~~~~ perl\nfour to start, three to end\n~~~</p>\n", + + "~~~ bash\ntildes\n~~~\n", + "<pre><code class=\"bash\">tildes\n</code></pre>\n", + + "``` lisp\nno ending\n", + "<p>``` lisp\nno ending</p>\n", + + "~~~ lisp\nend with language\n~~~ lisp\n", + "<p>~~~ lisp\nend with language\n~~~ lisp</p>\n", + + "```\nmismatched begin and end\n~~~\n", + "<p>```\nmismatched begin and end\n~~~</p>\n", + + "~~~\nmismatched begin and end\n```\n", + "<p>~~~\nmismatched begin and end\n```</p>\n", + + " ``` oz\nleading spaces\n```\n", + "<pre><code class=\"oz\">leading spaces\n</code></pre>\n", + + " ``` oz\nleading spaces\n ```\n", + "<pre><code class=\"oz\">leading spaces\n</code></pre>\n", + + " ``` oz\nleading spaces\n ```\n", + "<pre><code class=\"oz\">leading spaces\n</code></pre>\n", + + "``` oz\nleading spaces\n ```\n", + "<pre><code class=\"oz\">leading spaces\n</code></pre>\n", + + " ``` oz\nleading spaces\n ```\n", + "<pre><code>``` oz\n</code></pre>\n\n<p>leading spaces</p>\n\n<pre><code>```\n</code></pre>\n", + } + doTestsBlock(t, tests, EXTENSION_FENCED_CODE|EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) +}
M
inline.go
→
inline.go
@@ -620,7 +620,7 @@ }
// scan backward for a word boundary rewind := 0 - for offset-rewind > 0 && rewind <= 7 && !isspace(data[offset-rewind-1]) && !isspace(data[offset-rewind-1]) { + for offset-rewind > 0 && rewind <= 7 && isletter(data[offset-rewind-1]) { rewind++ } if rewind > 6 { // longest supported protocol is "mailto" which has 6 letters
M
inline_test.go
→
inline_test.go
@@ -462,6 +462,45 @@ }
func TestAutoLink(t *testing.T) { var tests = []string{ + "http://foo.com/\n", + "<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "1 http://foo.com/\n", + "<p>1 <a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "1http://foo.com/\n", + "<p>1<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "1.http://foo.com/\n", + "<p>1.<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "1. http://foo.com/\n", + "<ol>\n<li><a href=\"http://foo.com/\">http://foo.com/</a></li>\n</ol>\n", + + "-http://foo.com/\n", + "<p>-<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "- http://foo.com/\n", + "<ul>\n<li><a href=\"http://foo.com/\">http://foo.com/</a></li>\n</ul>\n", + + "_http://foo.com/\n", + "<p>_<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "令狐http://foo.com/\n", + "<p>令狐<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "令狐 http://foo.com/\n", + "<p>令狐 <a href=\"http://foo.com/\">http://foo.com/</a></p>\n", + + "ahttp://foo.com/\n", + "<p>ahttp://foo.com/</p>\n", + + ">http://foo.com/\n", + "<blockquote>\n<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n</blockquote>\n", + + "> http://foo.com/\n", + "<blockquote>\n<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n</blockquote>\n", + "go to <http://foo.com/>\n", "<p>go to <a href=\"http://foo.com/\">http://foo.com/</a></p>\n",
M
markdown.go
→
markdown.go
@@ -28,16 +28,17 @@
// These are the supported markdown parsing extensions. // OR these values together to select multiple extensions. const ( - EXTENSION_NO_INTRA_EMPHASIS = 1 << iota // ignore emphasis markers inside words - EXTENSION_TABLES // render tables - EXTENSION_FENCED_CODE // render fenced code blocks - EXTENSION_AUTOLINK // detect embedded URLs that are not explicitly marked - EXTENSION_STRIKETHROUGH // strikethrough text using ~~test~~ - EXTENSION_LAX_HTML_BLOCKS // loosen up HTML block parsing rules - EXTENSION_SPACE_HEADERS // be strict about prefix header rules - EXTENSION_HARD_LINE_BREAK // translate newlines into line breaks - EXTENSION_TAB_SIZE_EIGHT // expand tabs to eight spaces instead of four - EXTENSION_FOOTNOTES // Pandoc-style footnotes + EXTENSION_NO_INTRA_EMPHASIS = 1 << iota // ignore emphasis markers inside words + EXTENSION_TABLES // render tables + EXTENSION_FENCED_CODE // render fenced code blocks + EXTENSION_AUTOLINK // detect embedded URLs that are not explicitly marked + EXTENSION_STRIKETHROUGH // strikethrough text using ~~test~~ + EXTENSION_LAX_HTML_BLOCKS // loosen up HTML block parsing rules + EXTENSION_SPACE_HEADERS // be strict about prefix header rules + EXTENSION_HARD_LINE_BREAK // translate newlines into line breaks + EXTENSION_TAB_SIZE_EIGHT // expand tabs to eight spaces instead of four + EXTENSION_FOOTNOTES // Pandoc-style footnotes + EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK // No need to insert an empty line to start a (code, quote, order list, unorder list)block ) // These are the possible flag values for the link renderer.@@ -684,10 +685,15 @@ func isspace(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' } +// Test if a character is letter. +func isletter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + // Test if a character is a letter or a digit. // TODO: check when this is looking for ASCII alnum and when it should use unicode func isalnum(c byte) bool { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + return (c >= '0' && c <= '9') || isletter(c) } // Replace tab characters with spaces, aligning to the next TAB_SIZE column.
A
upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.html
@@ -0,0 +1,14 @@
+<p>In Markdown 1.0.0 and earlier. Version</p> + +<ol> +<li>This line turns into a list item. +Because a hard-wrapped line in the +middle of a paragraph looked like a +list item.</li> +</ol> + +<p>Here's one with a bullet.</p> + +<ul> +<li>criminey.</li> +</ul>
A
upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.text
@@ -0,0 +1,8 @@
+In Markdown 1.0.0 and earlier. Version +8. This line turns into a list item. +Because a hard-wrapped line in the +middle of a paragraph looked like a +list item. + +Here's one with a bullet. +* criminey.
M
upskirtref_test.go
→
upskirtref_test.go
@@ -19,12 +19,12 @@ "path/filepath"
"testing" ) -func runMarkdownReference(input string) string { +func runMarkdownReference(input string, flag int) string { renderer := HtmlRenderer(0, "", "") - return string(Markdown([]byte(input), renderer, 0)) + return string(Markdown([]byte(input), renderer, flag)) } -func doTestsReference(t *testing.T, files []string) { +func doTestsReference(t *testing.T, files []string, flag int) { // catch and report panics var candidate string defer func() {@@ -50,7 +50,7 @@ continue
} expected := string(expectedBytes) - actual := string(runMarkdownReference(input)) + actual := string(runMarkdownReference(input, flag)) if actual != expected { t.Errorf("\n [%#v]\nExpected[%#v]\nActual [%#v]", basename+".text", expected, actual)@@ -62,7 +62,7 @@ if !testing.Short() {
start := 0 for end := start + 1; end <= len(input); end++ { candidate = input[start:end] - _ = runMarkdownReference(candidate) + _ = runMarkdownReference(candidate, flag) } } }@@ -93,5 +93,33 @@ "Strong and em together",
"Tabs", "Tidyness", } - doTestsReference(t, files) + doTestsReference(t, files, 0) +} + +func TestReference_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { + files := []string{ + "Amps and angle encoding", + "Auto links", + "Backslash escapes", + "Blockquotes with code blocks", + "Code Blocks", + "Code Spans", + "Hard-wrapped paragraphs with list-like lines no empty line before block", + "Horizontal rules", + "Inline HTML (Advanced)", + "Inline HTML (Simple)", + "Inline HTML comments", + "Links, inline style", + "Links, reference style", + "Links, shortcut references", + "Literal quotes in titles", + "Markdown Documentation - Basics", + "Markdown Documentation - Syntax", + "Nested blockquotes", + "Ordered and unordered lists", + "Strong and em together", + "Tabs", + "Tidyness", + } + doTestsReference(t, files, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) }