From 3af64a90ad74a0c4bd13fc63b41b298b8a09ac21 Mon Sep 17 00:00:00 2001 From: Russ Ross Date: Mon, 27 Jun 2011 10:13:13 -0600 Subject: [PATCH] fixed headers nested in lists, added prefix header unit tests --- block.go | 39 +++++++---- block_test.go | 162 +++++++++++++++++++++++++++++++++++++++++++++ inline_test.go | 26 ++++---- upskirtref_test.go | 16 ++--- 4 files changed, 205 insertions(+), 38 deletions(-) create mode 100644 block_test.go diff --git a/block.go b/block.go index 538817b..17bf17c 100644 --- a/block.go +++ b/block.go @@ -940,7 +940,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int beg = end // process the following lines - in_empty, has_inside_empty := false, false + contains_blank_line, contains_block := false, false for beg < len(data) { end++ @@ -950,7 +950,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int // process an empty line if isEmpty(data[beg:end]) > 0 { - in_empty = true + contains_blank_line = true beg = end continue } @@ -967,11 +967,12 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int pre = 8 } - // check for a new item chunk := data[beg+i : end] + + // check for a nested list item if (blockUliPrefix(chunk) > 0 && !isHRule(chunk)) || blockOliPrefix(chunk) > 0 { - if in_empty { - has_inside_empty = true + if contains_blank_line { + contains_block = true } if pre == orgpre { // the following item must have the same indentation @@ -982,19 +983,29 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int sublist = work.Len() } } else { - // only join indented stuff after empty lines - if in_empty && i < 4 && data[beg] != '\t' { - *flags |= LIST_ITEM_END_OF_LIST - break + // how about a nested prefix header? + if isPrefixHeader(rndr, chunk) { + // only nest headers that are indented + if contains_blank_line && i < 4 && data[beg] != '\t' { + *flags |= LIST_ITEM_END_OF_LIST + break + } + contains_block = true } else { - if in_empty { - work.WriteByte('\n') - has_inside_empty = true + // only join stuff after empty lines when indented + if contains_blank_line && i < 4 && data[beg] != '\t' { + *flags |= LIST_ITEM_END_OF_LIST + break + } else { + if contains_blank_line { + work.WriteByte('\n') + contains_block = true + } } } } - in_empty = false + contains_blank_line = false // add the line into the working buffer without prefix work.Write(data[beg+i : end]) @@ -1002,7 +1013,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int } // render li contents - if has_inside_empty { + if contains_block { *flags |= LIST_ITEM_CONTAINS_BLOCK } diff --git a/block_test.go b/block_test.go new file mode 100644 index 0000000..57e865d --- /dev/null +++ b/block_test.go @@ -0,0 +1,162 @@ +// +// Black Friday Markdown Processor +// Originally based on http://github.com/tanoku/upskirt +// by Russ Ross +// + +// +// Unit tests for block parsing +// + +package blackfriday + +import ( + "testing" +) + +func runMarkdownBlock(input string, extensions uint32) string { + html_flags := 0 + html_flags |= HTML_USE_XHTML + + renderer := HtmlRenderer(html_flags) + + return string(Markdown([]byte(input), renderer, extensions)) +} + +func doTestsBlock(t *testing.T, tests []string, extensions uint32) { + for i := 0; i+1 < len(tests); i += 2 { + input := tests[i] + expected := tests[i+1] + actual := runMarkdownBlock(input, extensions) + if actual != expected { + t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", + input, expected, actual) + } + } +} + +func TestPrefixHeaderNoExtensions(t *testing.T) { + var tests = []string{ + "# Header 1\n", + "

Header 1

\n", + + "## Header 2\n", + "

Header 2

\n", + + "### Header 3\n", + "

Header 3

\n", + + "#### Header 4\n", + "

Header 4

\n", + + "##### Header 5\n", + "
Header 5
\n", + + "###### Header 6\n", + "
Header 6
\n", + + "####### Header 7\n", + "
# Header 7
\n", + + "#Header 1\n", + "

Header 1

\n", + + "##Header 2\n", + "

Header 2

\n", + + "###Header 3\n", + "

Header 3

\n", + + "####Header 4\n", + "

Header 4

\n", + + "#####Header 5\n", + "
Header 5
\n", + + "######Header 6\n", + "
Header 6
\n", + + "#######Header 7\n", + "
#Header 7
\n", + + "Hello\n# Header 1\nGoodbye\n", + "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", + + "* List\n# Header\n* List\n", + "\n", + + "* List\n#Header\n* List\n", + "\n", + + "* List\n * Nested list\n # Nested header\n", + "\n", + + "* List\n * Sublist\n Not a header\n ------\n", + "\n", + } + doTestsBlock(t, tests, 0) +} + +func TestPrefixHeaderSpaceExtension(t *testing.T) { + var tests = []string{ + "# Header 1\n", + "

Header 1

\n", + + "## Header 2\n", + "

Header 2

\n", + + "### Header 3\n", + "

Header 3

\n", + + "#### Header 4\n", + "

Header 4

\n", + + "##### Header 5\n", + "
Header 5
\n", + + "###### Header 6\n", + "
Header 6
\n", + + "####### Header 7\n", + "

####### Header 7

\n", + + "#Header 1\n", + "

#Header 1

\n", + + "##Header 2\n", + "

##Header 2

\n", + + "###Header 3\n", + "

###Header 3

\n", + + "####Header 4\n", + "

####Header 4

\n", + + "#####Header 5\n", + "

#####Header 5

\n", + + "######Header 6\n", + "

######Header 6

\n", + + "#######Header 7\n", + "

#######Header 7

\n", + + "Hello\n# Header 1\nGoodbye\n", + "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", + + "* List\n# Header\n* List\n", + "\n", + + "* List\n#Header\n* List\n", + "\n", + + "* List\n * Nested list\n # Nested header\n", + "\n", + + "* List\n * Sublist\n Not a header\n ------\n", + "\n", + } + doTestsBlock(t, tests, EXTENSION_SPACE_HEADERS) +} diff --git a/inline_test.go b/inline_test.go index aa401ec..4f4f21e 100644 --- a/inline_test.go +++ b/inline_test.go @@ -14,7 +14,7 @@ import ( "testing" ) -func runMarkdown(input string) string { +func runMarkdownInline(input string) string { var extensions uint32 extensions |= EXTENSION_NO_INTRA_EMPHASIS extensions |= EXTENSION_TABLES @@ -35,11 +35,11 @@ func runMarkdown(input string) string { return string(Markdown([]byte(input), renderer, extensions)) } -func doTests(t *testing.T, tests []string) { +func doTestsInline(t *testing.T, tests []string) { for i := 0; i+1 < len(tests); i += 2 { input := tests[i] expected := tests[i+1] - actual := runMarkdown(input) + actual := runMarkdownInline(input) if actual != expected { t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", input, expected, actual) @@ -97,7 +97,7 @@ func TestEmphasis(t *testing.T) { "mix of *markers_\n", "

mix of *markers_

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestStrong(t *testing.T) { @@ -150,7 +150,7 @@ func TestStrong(t *testing.T) { "mix of **markers__\n", "

mix of **markers__

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestEmphasisMix(t *testing.T) { @@ -179,7 +179,7 @@ func TestEmphasisMix(t *testing.T) { "*improper **nesting* is** bad\n", "

improper **nesting is** bad

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestStrikeThrough(t *testing.T) { @@ -208,7 +208,7 @@ func TestStrikeThrough(t *testing.T) { "odd ~~number\nof~~ markers~~ here\n", "

odd number\nof markers~~ here

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestCodeSpan(t *testing.T) { @@ -246,7 +246,7 @@ func TestCodeSpan(t *testing.T) { "```multiple ticks `with` ticks inside```\n", "

multiple ticks `with` ticks inside

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestLineBreak(t *testing.T) { @@ -260,7 +260,7 @@ func TestLineBreak(t *testing.T) { "this has an \nextra space\n", "

this has an
\nextra space

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestInlineLink(t *testing.T) { @@ -355,7 +355,7 @@ func TestInlineLink(t *testing.T) { "[link](/url/&query)\n", "

link

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestReferenceLink(t *testing.T) { @@ -387,7 +387,7 @@ func TestReferenceLink(t *testing.T) { "[ref]\n [ref]: /url/ \"title\"\n", "

ref

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestTags(t *testing.T) { @@ -404,7 +404,7 @@ func TestTags(t *testing.T) { "a tag\n", "

a tag

\n", } - doTests(t, tests) + doTestsInline(t, tests) } func TestAutoLink(t *testing.T) { @@ -448,5 +448,5 @@ func TestAutoLink(t *testing.T) { "

even a > can be escaped " + "http://new.com?q=>&etc

\n", } - doTests(t, tests) + doTestsInline(t, tests) } diff --git a/upskirtref_test.go b/upskirtref_test.go index 72c8b37..74e2be9 100644 --- a/upskirtref_test.go +++ b/upskirtref_test.go @@ -13,21 +13,15 @@ package blackfriday import ( "io/ioutil" "path/filepath" - "strings" "testing" ) -func runReferenceMarkdown(input string) string { +func runMarkdownReference(input string) string { renderer := HtmlRenderer(0) return string(Markdown([]byte(input), renderer, 0)) } -// disregard dos vs. unix line endings differences -func normalizeEol(s string) string { - return strings.Replace(s, "\r\n", "\n", -1) -} - -func doFileTests(t *testing.T, files []string) { +func doTestsReference(t *testing.T, files []string) { for _, basename := range files { fn := filepath.Join("upskirtref", basename+".text") actualdata, err := ioutil.ReadFile(fn) @@ -43,8 +37,8 @@ func doFileTests(t *testing.T, files []string) { } actual := string(actualdata) - actual = normalizeEol(string(runReferenceMarkdown(actual))) - expected := normalizeEol(string(expecteddata)) + actual = string(runMarkdownReference(actual)) + expected := string(expecteddata) if actual != expected { t.Errorf("\n [%#v]\nExpected[%#v]\nActual [%#v]", basename+".text", expected, actual) @@ -77,5 +71,5 @@ func TestReference(t *testing.T) { "Tabs", "Tidyness", } - doFileTests(t, files) + doTestsReference(t, files) }