mirror of
https://github.com/danog/blackfriday.git
synced 2024-11-26 12:04:46 +01:00
Add full info string in fenced code blocks (#449)
* Add full info string in fenced code blocks According to common mark, the info string for a fenced code block can be any non-whitespace string, so adjust the code to read a full string instead of just the syntax name. Fixes #410 in v2. * run go fmt
This commit is contained in:
parent
6aeb241ce2
commit
3420fef033
50
block.go
50
block.go
@ -17,6 +17,7 @@ import (
|
||||
"bytes"
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/shurcooL/sanitized_anchor_name"
|
||||
)
|
||||
@ -568,8 +569,8 @@ func (*Markdown) isHRule(data []byte) bool {
|
||||
|
||||
// isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data,
|
||||
// and returns the end index if so, or 0 otherwise. It also returns the marker found.
|
||||
// If syntax is not nil, it gets set to the syntax specified in the fence line.
|
||||
func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker string) {
|
||||
// If info is not nil, it gets set to the syntax specified in the fence line.
|
||||
func isFenceLine(data []byte, info *string, oldmarker string) (end int, marker string) {
|
||||
i, size := 0, 0
|
||||
|
||||
// skip up to three spaces
|
||||
@ -605,9 +606,9 @@ func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker
|
||||
}
|
||||
|
||||
// TODO(shurcooL): It's probably a good idea to simplify the 2 code paths here
|
||||
// into one, always get the syntax, and discard it if the caller doesn't care.
|
||||
if syntax != nil {
|
||||
syn := 0
|
||||
// into one, always get the info string, and discard it if the caller doesn't care.
|
||||
if info != nil {
|
||||
infoLength := 0
|
||||
i = skipChar(data, i, ' ')
|
||||
|
||||
if i >= len(data) {
|
||||
@ -617,14 +618,14 @@ func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
syntaxStart := i
|
||||
infoStart := i
|
||||
|
||||
if data[i] == '{' {
|
||||
i++
|
||||
syntaxStart++
|
||||
infoStart++
|
||||
|
||||
for i < len(data) && data[i] != '}' && data[i] != '\n' {
|
||||
syn++
|
||||
infoLength++
|
||||
i++
|
||||
}
|
||||
|
||||
@ -634,31 +635,30 @@ func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker
|
||||
|
||||
// strip all whitespace at the beginning and the end
|
||||
// of the {} block
|
||||
for syn > 0 && isspace(data[syntaxStart]) {
|
||||
syntaxStart++
|
||||
syn--
|
||||
for infoLength > 0 && isspace(data[infoStart]) {
|
||||
infoStart++
|
||||
infoLength--
|
||||
}
|
||||
|
||||
for syn > 0 && isspace(data[syntaxStart+syn-1]) {
|
||||
syn--
|
||||
for infoLength > 0 && isspace(data[infoStart+infoLength-1]) {
|
||||
infoLength--
|
||||
}
|
||||
|
||||
i++
|
||||
i = skipChar(data, i, ' ')
|
||||
} else {
|
||||
for i < len(data) && !isspace(data[i]) {
|
||||
syn++
|
||||
for i < len(data) && !isverticalspace(data[i]) {
|
||||
infoLength++
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
*syntax = string(data[syntaxStart : syntaxStart+syn])
|
||||
*info = strings.TrimSpace(string(data[infoStart : infoStart+infoLength]))
|
||||
}
|
||||
|
||||
i = skipChar(data, i, ' ')
|
||||
if i >= len(data) || data[i] != '\n' {
|
||||
if i == len(data) {
|
||||
return i, marker
|
||||
}
|
||||
if i == len(data) {
|
||||
return i, marker
|
||||
}
|
||||
if i > len(data) || data[i] != '\n' {
|
||||
return 0, ""
|
||||
}
|
||||
return i + 1, marker // Take newline into account.
|
||||
@ -668,14 +668,14 @@ func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker
|
||||
// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
|
||||
// If doRender is true, a final newline is mandatory to recognize the fenced code block.
|
||||
func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
|
||||
var syntax string
|
||||
beg, marker := isFenceLine(data, &syntax, "")
|
||||
var info string
|
||||
beg, marker := isFenceLine(data, &info, "")
|
||||
if beg == 0 || beg >= len(data) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var work bytes.Buffer
|
||||
work.Write([]byte(syntax))
|
||||
work.Write([]byte(info))
|
||||
work.WriteByte('\n')
|
||||
|
||||
for {
|
||||
|
@ -945,6 +945,9 @@ func TestFencedCodeBlock(t *testing.T) {
|
||||
"``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n",
|
||||
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
|
||||
|
||||
"``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
|
||||
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
|
||||
|
||||
"``` c\n/* special & char < > \" escaping */\n```\n",
|
||||
"<pre><code class=\"language-c\">/* special & char < > " escaping */\n</code></pre>\n",
|
||||
|
||||
@ -1403,6 +1406,9 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
|
||||
"``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n",
|
||||
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
|
||||
|
||||
"``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
|
||||
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
|
||||
|
||||
"``` c\n/* special & char < > \" escaping */\n```\n",
|
||||
"<pre><code class=\"language-c\">/* special & char < > " escaping */\n</code></pre>\n",
|
||||
|
||||
@ -1640,11 +1646,11 @@ func TestCompletePage(t *testing.T) {
|
||||
|
||||
func TestIsFenceLine(t *testing.T) {
|
||||
tests := []struct {
|
||||
data []byte
|
||||
syntaxRequested bool
|
||||
wantEnd int
|
||||
wantMarker string
|
||||
wantSyntax string
|
||||
data []byte
|
||||
infoRequested bool
|
||||
wantEnd int
|
||||
wantMarker string
|
||||
wantInfo string
|
||||
}{
|
||||
{
|
||||
data: []byte("```"),
|
||||
@ -1657,45 +1663,59 @@ func TestIsFenceLine(t *testing.T) {
|
||||
wantMarker: "```",
|
||||
},
|
||||
{
|
||||
data: []byte("```\nstuff here\n"),
|
||||
syntaxRequested: true,
|
||||
wantEnd: 4,
|
||||
wantMarker: "```",
|
||||
data: []byte("```\nstuff here\n"),
|
||||
infoRequested: true,
|
||||
wantEnd: 4,
|
||||
wantMarker: "```",
|
||||
},
|
||||
{
|
||||
data: []byte("stuff here\n```\n"),
|
||||
wantEnd: 0,
|
||||
},
|
||||
{
|
||||
data: []byte("```"),
|
||||
syntaxRequested: true,
|
||||
wantEnd: 3,
|
||||
wantMarker: "```",
|
||||
data: []byte("```"),
|
||||
infoRequested: true,
|
||||
wantEnd: 3,
|
||||
wantMarker: "```",
|
||||
},
|
||||
{
|
||||
data: []byte("``` go"),
|
||||
syntaxRequested: true,
|
||||
wantEnd: 6,
|
||||
wantMarker: "```",
|
||||
wantSyntax: "go",
|
||||
data: []byte("``` go"),
|
||||
infoRequested: true,
|
||||
wantEnd: 6,
|
||||
wantMarker: "```",
|
||||
wantInfo: "go",
|
||||
},
|
||||
{
|
||||
data: []byte("``` go foo bar"),
|
||||
infoRequested: true,
|
||||
wantEnd: 14,
|
||||
wantMarker: "```",
|
||||
wantInfo: "go foo bar",
|
||||
},
|
||||
{
|
||||
data: []byte("``` go foo bar "),
|
||||
infoRequested: true,
|
||||
wantEnd: 16,
|
||||
wantMarker: "```",
|
||||
wantInfo: "go foo bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var syntax *string
|
||||
if test.syntaxRequested {
|
||||
syntax = new(string)
|
||||
var info *string
|
||||
if test.infoRequested {
|
||||
info = new(string)
|
||||
}
|
||||
end, marker := isFenceLine(test.data, syntax, "```")
|
||||
end, marker := isFenceLine(test.data, info, "```")
|
||||
if got, want := end, test.wantEnd; got != want {
|
||||
t.Errorf("got end %v, want %v", got, want)
|
||||
}
|
||||
if got, want := marker, test.wantMarker; got != want {
|
||||
t.Errorf("got marker %q, want %q", got, want)
|
||||
}
|
||||
if test.syntaxRequested {
|
||||
if got, want := *syntax, test.wantSyntax; got != want {
|
||||
t.Errorf("got syntax %q, want %q", got, want)
|
||||
if test.infoRequested {
|
||||
if got, want := *info, test.wantInfo; got != want {
|
||||
t.Errorf("got info string %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
markdown.go
12
markdown.go
@ -813,7 +813,17 @@ func ispunct(c byte) bool {
|
||||
|
||||
// Test if a character is a whitespace character.
|
||||
func isspace(c byte) bool {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'
|
||||
return ishorizontalspace(c) || isverticalspace(c)
|
||||
}
|
||||
|
||||
// Test if a character is a horizontal whitespace character.
|
||||
func ishorizontalspace(c byte) bool {
|
||||
return c == ' ' || c == '\t'
|
||||
}
|
||||
|
||||
// Test if a character is a vertical character.
|
||||
func isverticalspace(c byte) bool {
|
||||
return c == '\n' || c == '\r' || c == '\f' || c == '\v'
|
||||
}
|
||||
|
||||
// Test if a character is letter.
|
||||
|
Loading…
Reference in New Issue
Block a user