mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
fenced code: ending marker must match beginning marker, tests for fenced code blocks
This commit is contained in:
parent
66b71b1f51
commit
2f839dc39a
49
block.go
49
block.go
|
@ -429,12 +429,13 @@ func isHRule(data []byte) bool {
|
||||||
return n >= 3
|
return n >= 3
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFencedCode(data []byte, syntax **string) int {
|
func isFencedCode(data []byte, syntax **string, oldmarker string) (skip int, marker string) {
|
||||||
i, n := 0, 0
|
i, size := 0, 0
|
||||||
|
skip = 0
|
||||||
|
|
||||||
// skip initial spaces
|
// skip initial spaces
|
||||||
if len(data) < 3 {
|
if len(data) < 3 {
|
||||||
return 0
|
return
|
||||||
}
|
}
|
||||||
if data[0] == ' ' {
|
if data[0] == ' ' {
|
||||||
i++
|
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] == '`') {
|
if i+2 >= len(data) || !(data[i] == '~' || data[i] == '`') {
|
||||||
return 0
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c := data[i]
|
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 {
|
for i < len(data) && data[i] == c {
|
||||||
n++
|
size++
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
if n < 3 {
|
// the marker char must occur at least 3 times
|
||||||
return 0
|
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 {
|
if syntax != nil {
|
||||||
|
@ -482,10 +490,10 @@ func isFencedCode(data []byte, syntax **string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == len(data) || data[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
|
// of the {} block
|
||||||
for syn > 0 && isspace(data[syntax_start]) {
|
for syn > 0 && isspace(data[syntax_start]) {
|
||||||
syntax_start++
|
syntax_start++
|
||||||
|
@ -508,19 +516,19 @@ func isFencedCode(data []byte, syntax **string) int {
|
||||||
*syntax = &language
|
*syntax = &language
|
||||||
}
|
}
|
||||||
|
|
||||||
for i < len(data) && data[i] != '\n' {
|
for ; i < len(data) && data[i] != '\n'; i++ {
|
||||||
if !isspace(data[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 {
|
func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
|
||||||
var lang *string
|
var lang *string
|
||||||
beg := isFencedCode(data, &lang)
|
beg, marker := isFencedCode(data, &lang, "")
|
||||||
if beg == 0 {
|
if beg == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -528,7 +536,7 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
|
||||||
var work bytes.Buffer
|
var work bytes.Buffer
|
||||||
|
|
||||||
for beg < len(data) {
|
for beg < len(data) {
|
||||||
fence_end := isFencedCode(data[beg:], nil)
|
fence_end, _ := isFencedCode(data[beg:], nil, marker)
|
||||||
if fence_end != 0 {
|
if fence_end != 0 {
|
||||||
beg += fence_end
|
beg += fence_end
|
||||||
break
|
break
|
||||||
|
@ -539,7 +547,7 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if beg < end {
|
if beg < end {
|
||||||
// verbatim copy to the working buffer, escaping entities
|
// verbatim copy to the working buffer
|
||||||
if isEmpty(data[beg:]) > 0 {
|
if isEmpty(data[beg:]) > 0 {
|
||||||
work.WriteByte('\n')
|
work.WriteByte('\n')
|
||||||
} else {
|
} else {
|
||||||
|
@ -547,6 +555,11 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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' {
|
if work.Len() > 0 && work.Bytes()[work.Len()-1] != '\n' {
|
||||||
|
|
|
@ -552,3 +552,74 @@ func TestPreformattedHtmlLax(t *testing.T) {
|
||||||
}
|
}
|
||||||
doTestsBlock(t, tests, EXTENSION_LAX_HTML_BLOCKS)
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user