fenced code: ending marker must match beginning marker, tests for fenced code blocks
Russ Ross russ@dixie.edu
Tue, 28 Jun 2011 10:30:25 -0600
2 files changed,
102 insertions(+),
18 deletions(-)
M
block.go
→
block.go
@@ -429,12 +429,13 @@
return n >= 3 } -func isFencedCode(data []byte, syntax **string) int { - i, n := 0, 0 +func isFencedCode(data []byte, syntax **string, oldmarker string) (skip int, marker string) { + i, size := 0, 0 + skip = 0 // skip initial spaces if len(data) < 3 { - return 0 + return } if data[0] == ' ' { i++@@ -446,21 +447,28 @@ }
} } - // look at the hrule char + // check for the marker characters: ~ or ` if i+2 >= len(data) || !(data[i] == '~' || data[i] == '`') { - return 0 + return } c := data[i] - // the whole line must be the char or whitespace + // the whole line must be the same char or whitespace for i < len(data) && data[i] == c { - n++ + size++ i++ } - if n < 3 { - return 0 + // the marker char must occur at least 3 times + if size < 3 { + return + } + marker = string(data[i-size : i]) + + // if this is the end marker, it must match the beginning marker + if oldmarker != "" && marker != oldmarker { + return } if syntax != nil {@@ -482,10 +490,10 @@ i++
} if i == len(data) || data[i] != '}' { - return 0 + return } - // string all whitespace at the beginning and the end + // strip all whitespace at the beginning and the end // of the {} block for syn > 0 && isspace(data[syntax_start]) { syntax_start++@@ -508,19 +516,19 @@ language := string(data[syntax_start : syntax_start+syn])
*syntax = &language } - for i < len(data) && data[i] != '\n' { + for ; i < len(data) && data[i] != '\n'; i++ { if !isspace(data[i]) { - return 0 + return } - i++ } - return i + 1 + skip = i + 1 + return } func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int { var lang *string - beg := isFencedCode(data, &lang) + beg, marker := isFencedCode(data, &lang, "") if beg == 0 { return 0 }@@ -528,7 +536,7 @@
var work bytes.Buffer for beg < len(data) { - fence_end := isFencedCode(data[beg:], nil) + fence_end, _ := isFencedCode(data[beg:], nil, marker) if fence_end != 0 { beg += fence_end break@@ -539,7 +547,7 @@ for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ {
} if beg < end { - // verbatim copy to the working buffer, escaping entities + // verbatim copy to the working buffer if isEmpty(data[beg:]) > 0 { work.WriteByte('\n') } else {@@ -547,6 +555,11 @@ work.Write(data[beg:end])
} } beg = end + + // did we find the end of the buffer without a closing marker? + if beg >= len(data) { + return 0 + } } if work.Len() > 0 && work.Bytes()[work.Len()-1] != '\n' {
M
block_test.go
→
block_test.go
@@ -552,3 +552,74 @@ "<p>Paragraph</p>\n\n<div>\nHow about here? >&<\n</div>\n\n<p>And here?</p>\n",
} doTestsBlock(t, tests, EXTENSION_LAX_HTML_BLOCKS) } + +func TestFencedCodeBlock(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\n ```</p>\n", + } + doTestsBlock(t, tests, EXTENSION_FENCED_CODE) +}