fenced code: ending marker must match beginning marker, tests for fenced code blocks

This commit is contained in:
Russ Ross 2011-06-28 10:30:25 -06:00
parent 66b71b1f51
commit 2f839dc39a
2 changed files with 102 additions and 18 deletions

View File

@ -429,12 +429,13 @@ func isHRule(data []byte) bool {
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 @@ func isFencedCode(data []byte, syntax **string) int {
}
}
// 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 @@ func isFencedCode(data []byte, syntax **string) int {
}
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 @@ func isFencedCode(data []byte, syntax **string) int {
*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 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
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 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
}
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 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
}
}
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' {

View File

@ -552,3 +552,74 @@ func TestPreformattedHtmlLax(t *testing.T) {
}
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 &amp; char &lt; &gt; &quot; 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)
}