diff --git a/block.go b/block.go index 7fc731d..929638a 100644 --- a/block.go +++ b/block.go @@ -15,6 +15,7 @@ package blackfriday import ( "bytes" + "strings" "unicode" ) @@ -92,7 +93,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // fenced code block: // - // ``` go + // ``` go info string here // func fact(n int) int { // if n <= 1 { // return n @@ -562,7 +563,7 @@ func (*parser) isHRule(data []byte) bool { // 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. // A final newline is mandatory to recognize the fence line, unless newlineOptional is true. -func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional bool) (end int, marker string) { +func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bool) (end int, marker string) { i, size := 0, 0 // skip up to three spaces @@ -598,9 +599,9 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional } // 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) { @@ -610,14 +611,14 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional return 0, "" } - syntaxStart := i + infoStart := i if data[i] == '{' { i++ - syntaxStart++ + infoStart++ for i < len(data) && data[i] != '}' && data[i] != '\n' { - syn++ + infoLength++ i++ } @@ -627,24 +628,24 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional // 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++ } 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, ' ') @@ -662,8 +663,8 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional // 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 *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) int { - var syntax string - beg, marker := isFenceLine(data, &syntax, "", false) + var infoString string + beg, marker := isFenceLine(data, &infoString, "", false) if beg == 0 || beg >= len(data) { return 0 } @@ -697,7 +698,7 @@ func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) } if doRender { - p.r.BlockCode(out, work.Bytes(), syntax) + p.r.BlockCode(out, work.Bytes(), infoString) } return beg diff --git a/block_test.go b/block_test.go index 89d5775..b9d6653 100644 --- a/block_test.go +++ b/block_test.go @@ -1053,6 +1053,9 @@ func TestFencedCodeBlock(t *testing.T) { "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", "
func foo() bool {\n\treturn true;\n}\n
\n",
+ "``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
+ "func foo() bool {\n\treturn true;\n}\n
\n",
+
"``` c\n/* special & char < > \" escaping */\n```\n",
"/* special & char < > " escaping */\n
\n",
@@ -1511,6 +1514,9 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
"``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n",
"func foo() bool {\n\treturn true;\n}\n
\n",
+ "``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
+ "func foo() bool {\n\treturn true;\n}\n
\n",
+
"``` c\n/* special & char < > \" escaping */\n```\n",
"/* special & char < > " escaping */\n
\n",
@@ -1646,11 +1652,11 @@ func TestCDATA(t *testing.T) {
func TestIsFenceLine(t *testing.T) {
tests := []struct {
data []byte
- syntaxRequested bool
+ infoRequested bool
newlineOptional bool
wantEnd int
wantMarker string
- wantSyntax string
+ wantInfo string
}{
{
data: []byte("```"),
@@ -1662,10 +1668,10 @@ 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"),
@@ -1679,36 +1685,52 @@ func TestIsFenceLine(t *testing.T) {
},
{
data: []byte("```"),
- syntaxRequested: true,
+ infoRequested: true,
newlineOptional: true,
wantEnd: 3,
wantMarker: "```",
},
{
data: []byte("``` go"),
- syntaxRequested: true,
+ infoRequested: true,
newlineOptional: true,
wantEnd: 6,
wantMarker: "```",
- wantSyntax: "go",
+ wantInfo: "go",
+ },
+ {
+ data: []byte("``` go foo bar"),
+ infoRequested: true,
+ newlineOptional: true,
+ wantEnd: 14,
+ wantMarker: "```",
+ wantInfo: "go foo bar",
+ },
+ {
+ data: []byte("``` go foo bar "),
+ infoRequested: true,
+ newlineOptional: 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, "```", test.newlineOptional)
+ end, marker := isFenceLine(test.data, info, "```", test.newlineOptional)
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 %q, want %q", got, want)
}
}
}
diff --git a/html.go b/html.go
index c917c7d..e0a6c69 100644
--- a/html.go
+++ b/html.go
@@ -255,33 +255,21 @@ func (options *Html) HRule(out *bytes.Buffer) {
out.WriteByte('\n')
}
-func (options *Html) BlockCode(out *bytes.Buffer, text []byte, lang string) {
+func (options *Html) BlockCode(out *bytes.Buffer, text []byte, info string) {
doubleSpace(out)
- // parse out the language names/classes
- count := 0
- for _, elt := range strings.Fields(lang) {
- if elt[0] == '.' {
- elt = elt[1:]
- }
- if len(elt) == 0 {
- continue
- }
- if count == 0 {
- out.WriteString("")
} else {
+ out.WriteString("")
}
-
attrEscape(out, text)
out.WriteString("
\n")
}
diff --git a/latex.go b/latex.go
index 70705aa..3d30d09 100644
--- a/latex.go
+++ b/latex.go
@@ -17,6 +17,7 @@ package blackfriday
import (
"bytes"
+ "strings"
)
// Latex is a type that implements the Renderer interface for LaTeX output.
@@ -39,16 +40,17 @@ func (options *Latex) GetFlags() int {
}
// render code chunks using verbatim, or listings if we have a language
-func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, lang string) {
- if lang == "" {
+func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, info string) {
+ if info == "" {
out.WriteString("\n\\begin{verbatim}\n")
} else {
+ lang := strings.Fields(info)[0]
out.WriteString("\n\\begin{lstlisting}[language=")
out.WriteString(lang)
out.WriteString("]\n")
}
out.Write(text)
- if lang == "" {
+ if info == "" {
out.WriteString("\n\\end{verbatim}\n")
} else {
out.WriteString("\n\\end{lstlisting}\n")
diff --git a/markdown.go b/markdown.go
index 1722a73..41595d6 100644
--- a/markdown.go
+++ b/markdown.go
@@ -159,7 +159,7 @@ var blockTags = map[string]struct{}{
// Currently Html and Latex implementations are provided
type Renderer interface {
// block-level callbacks
- BlockCode(out *bytes.Buffer, text []byte, lang string)
+ BlockCode(out *bytes.Buffer, text []byte, infoString string)
BlockQuote(out *bytes.Buffer, text []byte)
BlockHtml(out *bytes.Buffer, text []byte)
Header(out *bytes.Buffer, text func() bool, level int, id string)
@@ -804,7 +804,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 whitespace character.
+func isverticalspace(c byte) bool {
+ return c == '\n' || c == '\r' || c == '\f' || c == '\v'
}
// Test if a character is letter.