mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
Merge pull request #211 from russross/issue-122
Fix issue 122: fenced code blocks inside blockquotes
This commit is contained in:
commit
9c9c590f7e
36
block.go
36
block.go
|
@ -891,13 +891,35 @@ func (p *parser) quotePrefix(data []byte) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// blockquote ends with at least one blank line
|
||||
// followed by something without a blockquote prefix
|
||||
func (p *parser) terminateBlockquote(data []byte, beg, end int) bool {
|
||||
if p.isEmpty(data[beg:]) <= 0 {
|
||||
return false
|
||||
}
|
||||
if end >= len(data) {
|
||||
return true
|
||||
}
|
||||
return p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0
|
||||
}
|
||||
|
||||
// parse a blockquote fragment
|
||||
func (p *parser) quote(out *bytes.Buffer, data []byte) int {
|
||||
var raw bytes.Buffer
|
||||
beg, end := 0, 0
|
||||
for beg < len(data) {
|
||||
end = beg
|
||||
// Step over whole lines, collecting them. While doing that, check for
|
||||
// fenced code and if one's found, incorporate it altogether,
|
||||
// irregardless of any contents inside it
|
||||
for data[end] != '\n' {
|
||||
if p.flags&EXTENSION_FENCED_CODE != 0 {
|
||||
if i := p.fencedCode(out, data[end:], false); i > 0 {
|
||||
// -1 to compensate for the extra end++ after the loop:
|
||||
end += i - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
end++
|
||||
}
|
||||
end++
|
||||
|
@ -905,11 +927,7 @@ func (p *parser) quote(out *bytes.Buffer, data []byte) int {
|
|||
if pre := p.quotePrefix(data[beg:]); pre > 0 {
|
||||
// skip the prefix
|
||||
beg += pre
|
||||
} else if p.isEmpty(data[beg:]) > 0 &&
|
||||
(end >= len(data) ||
|
||||
(p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0)) {
|
||||
// blockquote ends with at least one blank line
|
||||
// followed by something without a blockquote prefix
|
||||
} else if p.terminateBlockquote(data, beg, end) {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -1342,6 +1360,14 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int {
|
|||
return i
|
||||
}
|
||||
|
||||
// if there's a fenced code block, paragraph is over
|
||||
if p.flags&EXTENSION_FENCED_CODE != 0 {
|
||||
if p.fencedCode(out, current, false) > 0 {
|
||||
p.renderParagraph(out, data[:i])
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a definition list item, prev line is a definition term
|
||||
if p.flags&EXTENSION_DEFINITION_LISTS != 0 {
|
||||
if p.dliPrefix(current) != 0 {
|
||||
|
|
113
block_test.go
113
block_test.go
|
@ -14,6 +14,7 @@
|
|||
package blackfriday
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -1065,6 +1066,118 @@ func TestFencedCodeBlock(t *testing.T) {
|
|||
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
|
||||
}
|
||||
|
||||
func TestFencedCodeInsideBlockquotes(t *testing.T) {
|
||||
cat := func(s ...string) string { return strings.Join(s, "\n") }
|
||||
var tests = []string{
|
||||
cat("> ```go",
|
||||
"package moo",
|
||||
"",
|
||||
"```",
|
||||
""),
|
||||
`<blockquote>
|
||||
<pre><code class="language-go">package moo
|
||||
|
||||
</code></pre>
|
||||
</blockquote>
|
||||
`,
|
||||
// -------------------------------------------
|
||||
cat("> foo",
|
||||
"> ",
|
||||
"> ```go",
|
||||
"package moo",
|
||||
"```",
|
||||
"> ",
|
||||
"> goo.",
|
||||
""),
|
||||
`<blockquote>
|
||||
<p>foo</p>
|
||||
|
||||
<pre><code class="language-go">package moo
|
||||
</code></pre>
|
||||
|
||||
<p>goo.</p>
|
||||
</blockquote>
|
||||
`,
|
||||
// -------------------------------------------
|
||||
cat("> foo",
|
||||
"> ",
|
||||
"> quote",
|
||||
"continues",
|
||||
"```",
|
||||
""),
|
||||
`<blockquote>
|
||||
<p>foo</p>
|
||||
|
||||
<p>quote
|
||||
continues
|
||||
` + "```" + `</p>
|
||||
</blockquote>
|
||||
`,
|
||||
// -------------------------------------------
|
||||
cat("> foo",
|
||||
"> ",
|
||||
"> ```go",
|
||||
"package moo",
|
||||
"```",
|
||||
"> ",
|
||||
"> goo.",
|
||||
"> ",
|
||||
"> ```go",
|
||||
"package zoo",
|
||||
"```",
|
||||
"> ",
|
||||
"> woo.",
|
||||
""),
|
||||
`<blockquote>
|
||||
<p>foo</p>
|
||||
|
||||
<pre><code class="language-go">package moo
|
||||
</code></pre>
|
||||
|
||||
<p>goo.</p>
|
||||
|
||||
<pre><code class="language-go">package zoo
|
||||
</code></pre>
|
||||
|
||||
<p>woo.</p>
|
||||
</blockquote>
|
||||
`,
|
||||
}
|
||||
|
||||
// These 2 alternative forms of blockquoted fenced code blocks should produce same output.
|
||||
forms := [2]string{
|
||||
cat("> plain quoted text",
|
||||
"> ```fenced",
|
||||
"code",
|
||||
" with leading single space correctly preserved",
|
||||
"okay",
|
||||
"```",
|
||||
"> rest of quoted text"),
|
||||
cat("> plain quoted text",
|
||||
"> ```fenced",
|
||||
"> code",
|
||||
"> with leading single space correctly preserved",
|
||||
"> okay",
|
||||
"> ```",
|
||||
"> rest of quoted text"),
|
||||
}
|
||||
want := `<blockquote>
|
||||
<p>plain quoted text</p>
|
||||
|
||||
<pre><code class="language-fenced">code
|
||||
with leading single space correctly preserved
|
||||
okay
|
||||
</code></pre>
|
||||
|
||||
<p>rest of quoted text</p>
|
||||
</blockquote>
|
||||
`
|
||||
tests = append(tests, forms[0], want)
|
||||
tests = append(tests, forms[1], want)
|
||||
|
||||
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
|
||||
}
|
||||
|
||||
func TestTable(t *testing.T) {
|
||||
var tests = []string{
|
||||
"a | b\n---|---\nc | d\n",
|
||||
|
|
|
@ -385,7 +385,6 @@ func MarkdownOptions(input []byte, renderer Renderer, opts Options) []byte {
|
|||
// - expand tabs
|
||||
// - normalize newlines
|
||||
// - copy everything else
|
||||
// - add missing newlines before fenced code blocks
|
||||
func firstPass(p *parser, input []byte) []byte {
|
||||
var out bytes.Buffer
|
||||
tabSize := TAB_SIZE_DEFAULT
|
||||
|
@ -393,7 +392,6 @@ func firstPass(p *parser, input []byte) []byte {
|
|||
tabSize = TAB_SIZE_EIGHT
|
||||
}
|
||||
beg, end := 0, 0
|
||||
lastLineWasBlank := false
|
||||
lastFencedCodeBlockEnd := 0
|
||||
for beg < len(input) { // iterate over lines
|
||||
if end = isReference(p, input[beg:], tabSize); end > 0 {
|
||||
|
@ -405,16 +403,13 @@ func firstPass(p *parser, input []byte) []byte {
|
|||
}
|
||||
|
||||
if p.flags&EXTENSION_FENCED_CODE != 0 {
|
||||
// when last line was none blank and a fenced code block comes after
|
||||
// track fenced code block boundaries to suppress tab expansion
|
||||
// inside them:
|
||||
if beg >= lastFencedCodeBlockEnd {
|
||||
if i := p.fencedCode(&out, input[beg:], false); i > 0 {
|
||||
if !lastLineWasBlank {
|
||||
out.WriteByte('\n') // need to inject additional linebreak
|
||||
}
|
||||
lastFencedCodeBlockEnd = beg + i
|
||||
}
|
||||
}
|
||||
lastLineWasBlank = end == beg
|
||||
}
|
||||
|
||||
// add the line body if present
|
||||
|
|
Loading…
Reference in New Issue
Block a user