all repos — grayfriday @ 9c9c590f7ec7f639ca6d885ecb0659f9f61fa7fc

blackfriday fork with a few changes

Merge pull request #211 from russross/issue-122

Fix issue 122: fenced code blocks inside blockquotes
Vytautas Ĺ altenis vytas@rtfb.lt
Sun, 01 Nov 2015 09:44:04 +0200
commit

9c9c590f7ec7f639ca6d885ecb0659f9f61fa7fc

parent

ceb8293c340b3ea2025635db9a0facd98919c495

3 files changed, 146 insertions(+), 12 deletions(-)

jump to
M block.goblock.go

@@ -891,13 +891,35 @@ }

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 @@

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 }

@@ -1340,6 +1358,14 @@ // if there's a prefixed header or a horizontal rule after this, paragraph is over

if p.isPrefixHeader(current) || p.isHRule(current) { p.renderParagraph(out, data[:i]) 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
M block_test.goblock_test.go

@@ -14,6 +14,7 @@

package blackfriday import ( + "strings" "testing" )

@@ -1062,6 +1063,118 @@

"Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nSome text in between\n``` oz\nmultiple code blocks work okay\n```\nAnd some text after a fenced code block", "<p>Some text before a fenced code block</p>\n\n<pre><code class=\"language-oz\">code blocks breakup paragraphs\n</code></pre>\n\n<p>Some text in between</p>\n\n<pre><code class=\"language-oz\">multiple code blocks work okay\n</code></pre>\n\n<p>And some text after a fenced code block</p>\n", } + 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) }
M markdown.gomarkdown.go

@@ -385,7 +385,6 @@ // - extract references

// - 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 @@ if p.flags&EXTENSION_TAB_SIZE_EIGHT != 0 {

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 @@ end++

} 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