mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
Merge pull request #284 from russross/fix-lint
Fix most of lint errors on v2
This commit is contained in:
commit
ca4bf013e8
25
block.go
25
block.go
|
@ -22,13 +22,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Entity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
|
charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
|
||||||
Escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
|
escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reBackslashOrAmp = regexp.MustCompile("[\\&]")
|
reBackslashOrAmp = regexp.MustCompile("[\\&]")
|
||||||
reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + Escapable + "|" + Entity)
|
reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity)
|
||||||
reTrailingWhitespace = regexp.MustCompile("(\n *)+$")
|
reTrailingWhitespace = regexp.MustCompile("(\n *)+$")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -279,9 +279,8 @@ func (p *parser) isUnderlinedHeader(data []byte) int {
|
||||||
i = skipChar(data, i, ' ')
|
i = skipChar(data, i, ' ')
|
||||||
if data[i] == '\n' {
|
if data[i] == '\n' {
|
||||||
return 1
|
return 1
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// test of level 2 header
|
// test of level 2 header
|
||||||
|
@ -290,9 +289,8 @@ func (p *parser) isUnderlinedHeader(data []byte) int {
|
||||||
i = skipChar(data, i, ' ')
|
i = skipChar(data, i, ' ')
|
||||||
if data[i] == '\n' {
|
if data[i] == '\n' {
|
||||||
return 2
|
return 2
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -414,20 +412,20 @@ func (p *parser) html(data []byte, doRender bool) int {
|
||||||
for end > 0 && data[end-1] == '\n' {
|
for end > 0 && data[end-1] == '\n' {
|
||||||
end--
|
end--
|
||||||
}
|
}
|
||||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, data[:end]))
|
finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func finalizeHtmlBlock(block *Node) {
|
func finalizeHTMLBlock(block *Node) {
|
||||||
block.Literal = reTrailingWhitespace.ReplaceAll(block.content, []byte{})
|
block.Literal = reTrailingWhitespace.ReplaceAll(block.content, []byte{})
|
||||||
block.content = []byte{}
|
block.content = []byte{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML comment, lax form
|
// HTML comment, lax form
|
||||||
func (p *parser) htmlComment(data []byte, doRender bool) int {
|
func (p *parser) htmlComment(data []byte, doRender bool) int {
|
||||||
i := p.inlineHtmlComment(data)
|
i := p.inlineHTMLComment(data)
|
||||||
// needs to end with a blank line
|
// needs to end with a blank line
|
||||||
if j := p.isEmpty(data[i:]); j > 0 {
|
if j := p.isEmpty(data[i:]); j > 0 {
|
||||||
size := i + j
|
size := i + j
|
||||||
|
@ -438,7 +436,7 @@ func (p *parser) htmlComment(data []byte, doRender bool) int {
|
||||||
end--
|
end--
|
||||||
}
|
}
|
||||||
block := p.addBlock(HTMLBlock, data[:end])
|
block := p.addBlock(HTMLBlock, data[:end])
|
||||||
finalizeHtmlBlock(block)
|
finalizeHTMLBlock(block)
|
||||||
}
|
}
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
@ -470,7 +468,7 @@ func (p *parser) htmlHr(data []byte, doRender bool) int {
|
||||||
for end > 0 && data[end-1] == '\n' {
|
for end > 0 && data[end-1] == '\n' {
|
||||||
end--
|
end--
|
||||||
}
|
}
|
||||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, data[:end]))
|
finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||||
}
|
}
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
@ -729,9 +727,8 @@ func unescapeChar(str []byte) []byte {
|
||||||
func unescapeString(str []byte) []byte {
|
func unescapeString(str []byte) []byte {
|
||||||
if reBackslashOrAmp.Match(str) {
|
if reBackslashOrAmp.Match(str) {
|
||||||
return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar)
|
return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar)
|
||||||
} else {
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func finalizeCodeBlock(block *Node) {
|
func finalizeCodeBlock(block *Node) {
|
||||||
|
|
|
@ -1572,7 +1572,7 @@ func TestCompletePage(t *testing.T) {
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta name="GENERATOR" content="Blackfriday Markdown Processor v1.4" />
|
<meta name="GENERATOR" content="Blackfriday Markdown Processor v2.0" />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -54,7 +54,7 @@ func doTests(t *testing.T, tests []string) {
|
||||||
doTestsParam(t, tests, TestParams{
|
doTestsParam(t, tests, TestParams{
|
||||||
Options: DefaultOptions,
|
Options: DefaultOptions,
|
||||||
HTMLRendererParameters: HTMLRendererParameters{
|
HTMLRendererParameters: HTMLRendererParameters{
|
||||||
Flags: CommonHtmlFlags,
|
Flags: CommonHTMLFlags,
|
||||||
Extensions: CommonExtensions,
|
Extensions: CommonExtensions,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -106,7 +106,7 @@ func doLinkTestsInline(t *testing.T, tests []string) {
|
||||||
HTMLRendererParameters: params,
|
HTMLRendererParameters: params,
|
||||||
})
|
})
|
||||||
doTestsInlineParam(t, transformTests, TestParams{
|
doTestsInlineParam(t, transformTests, TestParams{
|
||||||
HTMLFlags: CommonHtmlFlags,
|
HTMLFlags: CommonHTMLFlags,
|
||||||
HTMLRendererParameters: params,
|
HTMLRendererParameters: params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
24
html.go
24
html.go
|
@ -24,6 +24,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HTMLFlags control optional behavior of HTML renderer.
|
||||||
type HTMLFlags int
|
type HTMLFlags int
|
||||||
|
|
||||||
// HTML renderer configuration options.
|
// HTML renderer configuration options.
|
||||||
|
@ -63,6 +64,8 @@ var (
|
||||||
htmlTagRe = regexp.MustCompile("(?i)^" + HTMLTag)
|
htmlTagRe = regexp.MustCompile("(?i)^" + HTMLTag)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HTMLRendererParameters is a collection of supplementary parameters tweaking
|
||||||
|
// the behavior of various parts of HTML renderer.
|
||||||
type HTMLRendererParameters struct {
|
type HTMLRendererParameters struct {
|
||||||
// Prepend this text to each relative URL.
|
// Prepend this text to each relative URL.
|
||||||
AbsolutePrefix string
|
AbsolutePrefix string
|
||||||
|
@ -126,8 +129,8 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHtmlTag(tag []byte, tagname string) bool {
|
func isHTMLTag(tag []byte, tagname string) bool {
|
||||||
found, _ := findHtmlTagPos(tag, tagname)
|
found, _ := findHTMLTagPos(tag, tagname)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +157,7 @@ func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
|
||||||
return start
|
return start
|
||||||
}
|
}
|
||||||
|
|
||||||
func findHtmlTagPos(tag []byte, tagname string) (bool, int) {
|
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
|
||||||
i := 0
|
i := 0
|
||||||
if i < len(tag) && tag[0] != '<' {
|
if i < len(tag) && tag[0] != '<' {
|
||||||
return false, -1
|
return false, -1
|
||||||
|
@ -384,6 +387,16 @@ func (r *HTMLRenderer) cr(w io.Writer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenderNode is a default renderer of a single node of a syntax tree. For
|
||||||
|
// block nodes it will be called twice: first time with entering=true, second
|
||||||
|
// time with entering=false, so that it could know when it's working on an open
|
||||||
|
// tag and when on close. It writes the result to w.
|
||||||
|
//
|
||||||
|
// The return value is a way to tell the calling walker to adjust its walk
|
||||||
|
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||||
|
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||||
|
// The typical behavior is to return GoToNext, which asks for the usual
|
||||||
|
// traversal to the next node.
|
||||||
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
||||||
attrs := []string{}
|
attrs := []string{}
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
|
@ -420,7 +433,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||||
if r.Flags&SkipHTML != 0 {
|
if r.Flags&SkipHTML != 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if r.Flags&SkipStyle != 0 && isHtmlTag(node.Literal, "style") {
|
if r.Flags&SkipStyle != 0 && isHTMLTag(node.Literal, "style") {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
//if options.safe {
|
//if options.safe {
|
||||||
|
@ -714,7 +727,7 @@ func (r *HTMLRenderer) writeDocumentHeader(w *bytes.Buffer, sr *SPRenderer) {
|
||||||
}
|
}
|
||||||
w.WriteString("</title>\n")
|
w.WriteString("</title>\n")
|
||||||
w.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
w.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
||||||
w.WriteString(VERSION)
|
w.WriteString(Version)
|
||||||
w.WriteString("\"")
|
w.WriteString("\"")
|
||||||
w.WriteString(ending)
|
w.WriteString(ending)
|
||||||
w.WriteString(">\n")
|
w.WriteString(">\n")
|
||||||
|
@ -739,6 +752,7 @@ func (r *HTMLRenderer) writeDocumentFooter(w *bytes.Buffer) {
|
||||||
w.WriteString("\n</body>\n</html>\n")
|
w.WriteString("\n</body>\n</html>\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render walks the specified syntax (sub)tree and returns a HTML document.
|
||||||
func (r *HTMLRenderer) Render(ast *Node) []byte {
|
func (r *HTMLRenderer) Render(ast *Node) []byte {
|
||||||
//println("render_Blackfriday")
|
//println("render_Blackfriday")
|
||||||
//dump(ast)
|
//dump(ast)
|
||||||
|
|
36
inline.go
36
inline.go
|
@ -266,13 +266,13 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
// ![alt] == image
|
// ![alt] == image
|
||||||
case offset >= 0 && data[offset] == '!':
|
case offset >= 0 && data[offset] == '!':
|
||||||
t = linkImg
|
t = linkImg
|
||||||
offset += 1
|
offset++
|
||||||
// ^[text] == inline footnote
|
// ^[text] == inline footnote
|
||||||
// [^refId] == deferred footnote
|
// [^refId] == deferred footnote
|
||||||
case p.flags&Footnotes != 0:
|
case p.flags&Footnotes != 0:
|
||||||
if offset >= 0 && data[offset] == '^' {
|
if offset >= 0 && data[offset] == '^' {
|
||||||
t = linkInlineFootnote
|
t = linkInlineFootnote
|
||||||
offset += 1
|
offset++
|
||||||
} else if len(data)-1 > offset && data[offset+1] == '^' {
|
} else if len(data)-1 > offset && data[offset+1] == '^' {
|
||||||
t = linkDeferredFootnote
|
t = linkDeferredFootnote
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
i = 1
|
i = 1
|
||||||
noteId int
|
noteID int
|
||||||
title, link, altContent []byte
|
title, link, altContent []byte
|
||||||
textHasNl = false
|
textHasNl = false
|
||||||
)
|
)
|
||||||
|
@ -501,7 +501,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
|
|
||||||
if t == linkInlineFootnote {
|
if t == linkInlineFootnote {
|
||||||
// create a new reference
|
// create a new reference
|
||||||
noteId = len(p.notes) + 1
|
noteID = len(p.notes) + 1
|
||||||
|
|
||||||
var fragment []byte
|
var fragment []byte
|
||||||
if len(id) > 0 {
|
if len(id) > 0 {
|
||||||
|
@ -512,11 +512,11 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
}
|
}
|
||||||
copy(fragment, slugify(id))
|
copy(fragment, slugify(id))
|
||||||
} else {
|
} else {
|
||||||
fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteId))...)
|
fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteID))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := &reference{
|
ref := &reference{
|
||||||
noteId: noteId,
|
noteID: noteID,
|
||||||
hasBlock: false,
|
hasBlock: false,
|
||||||
link: fragment,
|
link: fragment,
|
||||||
title: id,
|
title: id,
|
||||||
|
@ -534,7 +534,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if t == linkDeferredFootnote {
|
if t == linkDeferredFootnote {
|
||||||
lr.noteId = len(p.notes) + 1
|
lr.noteID = len(p.notes) + 1
|
||||||
p.notes = append(p.notes, lr)
|
p.notes = append(p.notes, lr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
link = lr.link
|
link = lr.link
|
||||||
// if inline footnote, title == footnote contents
|
// if inline footnote, title == footnote contents
|
||||||
title = lr.title
|
title = lr.title
|
||||||
noteId = lr.noteId
|
noteID = lr.noteID
|
||||||
}
|
}
|
||||||
|
|
||||||
// rewind the whitespace
|
// rewind the whitespace
|
||||||
|
@ -590,13 +590,13 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
linkNode.Title = title
|
linkNode.Title = title
|
||||||
p.currBlock.appendChild(linkNode)
|
p.currBlock.appendChild(linkNode)
|
||||||
linkNode.appendChild(text(data[1:txtE]))
|
linkNode.appendChild(text(data[1:txtE]))
|
||||||
i += 1
|
i++
|
||||||
|
|
||||||
case linkInlineFootnote, linkDeferredFootnote:
|
case linkInlineFootnote, linkDeferredFootnote:
|
||||||
linkNode := NewNode(Link)
|
linkNode := NewNode(Link)
|
||||||
linkNode.Destination = link
|
linkNode.Destination = link
|
||||||
linkNode.Title = title
|
linkNode.Title = title
|
||||||
linkNode.NoteID = noteId
|
linkNode.NoteID = noteID
|
||||||
p.currBlock.appendChild(linkNode)
|
p.currBlock.appendChild(linkNode)
|
||||||
if t == linkInlineFootnote {
|
if t == linkInlineFootnote {
|
||||||
i++
|
i++
|
||||||
|
@ -609,7 +609,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) inlineHtmlComment(data []byte) int {
|
func (p *parser) inlineHTMLComment(data []byte) int {
|
||||||
if len(data) < 5 {
|
if len(data) < 5 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -643,7 +643,7 @@ func leftAngle(p *parser, data []byte, offset int) int {
|
||||||
data = data[offset:]
|
data = data[offset:]
|
||||||
altype := LinkTypeNotAutolink
|
altype := LinkTypeNotAutolink
|
||||||
end := tagLength(data, &altype)
|
end := tagLength(data, &altype)
|
||||||
if size := p.inlineHtmlComment(data); size > 0 {
|
if size := p.inlineHTMLComment(data); size > 0 {
|
||||||
end = size
|
end = size
|
||||||
}
|
}
|
||||||
if end > 2 {
|
if end > 2 {
|
||||||
|
@ -1026,9 +1026,8 @@ func isMailtoAutoLink(data []byte) int {
|
||||||
case '>':
|
case '>':
|
||||||
if nb == 1 {
|
if nb == 1 {
|
||||||
return i + 1
|
return i + 1
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -1091,9 +1090,8 @@ func helperFindEmphChar(data []byte, c byte) int {
|
||||||
if data[i] != '[' && data[i] != '(' { // not a link
|
if data[i] != '[' && data[i] != '(' { // not a link
|
||||||
if tmpI > 0 {
|
if tmpI > 0 {
|
||||||
return tmpI
|
return tmpI
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
cc := data[i]
|
cc := data[i]
|
||||||
i++
|
i++
|
||||||
|
@ -1218,17 +1216,15 @@ func helperTripleEmphasis(p *parser, data []byte, offset int, c byte) int {
|
||||||
length = helperEmphasis(p, origData[offset-2:], c)
|
length = helperEmphasis(p, origData[offset-2:], c)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
|
||||||
return length - 2
|
|
||||||
}
|
}
|
||||||
|
return length - 2
|
||||||
default:
|
default:
|
||||||
// single symbol found, hand over to emph2
|
// single symbol found, hand over to emph2
|
||||||
length = helperDoubleEmphasis(p, origData[offset-1:], c)
|
length = helperDoubleEmphasis(p, origData[offset-1:], c)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
|
||||||
return length - 1
|
|
||||||
}
|
}
|
||||||
|
return length - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|
2
latex.go
2
latex.go
|
@ -311,7 +311,7 @@ func (r *Latex) DocumentHeader() {
|
||||||
r.w.WriteString(" pdfstartview=FitH,%\n")
|
r.w.WriteString(" pdfstartview=FitH,%\n")
|
||||||
r.w.WriteString(" breaklinks=true,%\n")
|
r.w.WriteString(" breaklinks=true,%\n")
|
||||||
r.w.WriteString(" pdfauthor={Blackfriday Markdown Processor v")
|
r.w.WriteString(" pdfauthor={Blackfriday Markdown Processor v")
|
||||||
r.w.WriteString(VERSION)
|
r.w.WriteString(Version)
|
||||||
r.w.WriteString("}}\n")
|
r.w.WriteString("}}\n")
|
||||||
r.w.WriteString("\n")
|
r.w.WriteString("\n")
|
||||||
r.w.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n")
|
r.w.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n")
|
||||||
|
|
49
markdown.go
49
markdown.go
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
// Blackfriday markdown processor.
|
// Package blackfriday is a markdown processor.
|
||||||
//
|
//
|
||||||
// Translates plain text with simple formatting rules into HTML or LaTeX.
|
// Translates plain text with simple formatting rules into HTML or LaTeX.
|
||||||
package blackfriday
|
package blackfriday
|
||||||
|
@ -26,8 +26,11 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.4"
|
// Version string of the package.
|
||||||
|
const Version = "2.0"
|
||||||
|
|
||||||
|
// Extensions is a bitwise or'ed collection of enabled Blackfriday's
|
||||||
|
// extensions.
|
||||||
type Extensions int
|
type Extensions int
|
||||||
|
|
||||||
// These are the supported markdown parsing extensions.
|
// These are the supported markdown parsing extensions.
|
||||||
|
@ -58,7 +61,7 @@ const (
|
||||||
TOC // Generate a table of contents
|
TOC // Generate a table of contents
|
||||||
OmitContents // Skip the main contents (for a standalone table of contents)
|
OmitContents // Skip the main contents (for a standalone table of contents)
|
||||||
|
|
||||||
CommonHtmlFlags HTMLFlags = UseXHTML
|
CommonHTMLFlags HTMLFlags = UseXHTML
|
||||||
|
|
||||||
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
|
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
|
||||||
Autolink | Strikethrough | SpaceHeaders | HeaderIDs |
|
Autolink | Strikethrough | SpaceHeaders | HeaderIDs |
|
||||||
|
@ -66,10 +69,13 @@ const (
|
||||||
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
|
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultOptions is a convenience variable with all the options that are
|
||||||
|
// enabled by default.
|
||||||
var DefaultOptions = Options{
|
var DefaultOptions = Options{
|
||||||
Extensions: CommonExtensions,
|
Extensions: CommonExtensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this should probably be unexported. Or moved to node.go
|
||||||
type LinkType int
|
type LinkType int
|
||||||
|
|
||||||
// These are the possible flag values for the link renderer.
|
// These are the possible flag values for the link renderer.
|
||||||
|
@ -81,6 +87,7 @@ const (
|
||||||
LinkTypeEmail
|
LinkTypeEmail
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ListType contains bitwise or'ed flags for list and list item objects.
|
||||||
type ListType int
|
type ListType int
|
||||||
|
|
||||||
// These are the possible flag values for the ListItem renderer.
|
// These are the possible flag values for the ListItem renderer.
|
||||||
|
@ -96,6 +103,7 @@ const (
|
||||||
ListItemEndOfList
|
ListItemEndOfList
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CellAlignFlags holds a type of alignment in a table cell.
|
||||||
type CellAlignFlags int
|
type CellAlignFlags int
|
||||||
|
|
||||||
// These are the possible flag values for the table cell renderer.
|
// These are the possible flag values for the table cell renderer.
|
||||||
|
@ -213,7 +221,7 @@ func (p *parser) getRef(refid string) (ref *reference, found bool) {
|
||||||
return &reference{
|
return &reference{
|
||||||
link: []byte(r.Link),
|
link: []byte(r.Link),
|
||||||
title: []byte(r.Title),
|
title: []byte(r.Title),
|
||||||
noteId: 0,
|
noteID: 0,
|
||||||
hasBlock: false,
|
hasBlock: false,
|
||||||
text: []byte(r.Text)}, true
|
text: []byte(r.Text)}, true
|
||||||
}
|
}
|
||||||
|
@ -312,9 +320,8 @@ func MarkdownBasic(input []byte) []byte {
|
||||||
return Markdown(input, renderer, Options{})
|
return Markdown(input, renderer, Options{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Markdown with most useful extensions enabled
|
// MarkdownCommon is a convenience function for simple rendering. It calls
|
||||||
// MarkdownCommon is a convenience function for simple rendering.
|
// Markdown with most useful extensions enabled, including:
|
||||||
// It processes markdown input with common extensions enabled, including:
|
|
||||||
//
|
//
|
||||||
// * Smartypants processing with smart fractions and LaTeX dashes
|
// * Smartypants processing with smart fractions and LaTeX dashes
|
||||||
//
|
//
|
||||||
|
@ -334,7 +341,7 @@ func MarkdownBasic(input []byte) []byte {
|
||||||
func MarkdownCommon(input []byte) []byte {
|
func MarkdownCommon(input []byte) []byte {
|
||||||
// set up the HTML renderer
|
// set up the HTML renderer
|
||||||
renderer := NewHTMLRenderer(HTMLRendererParameters{
|
renderer := NewHTMLRenderer(HTMLRendererParameters{
|
||||||
Flags: CommonHtmlFlags,
|
Flags: CommonHTMLFlags,
|
||||||
Extensions: CommonExtensions,
|
Extensions: CommonExtensions,
|
||||||
})
|
})
|
||||||
return Markdown(input, renderer, DefaultOptions)
|
return Markdown(input, renderer, DefaultOptions)
|
||||||
|
@ -354,6 +361,10 @@ func Markdown(input []byte, renderer Renderer, options Options) []byte {
|
||||||
return renderer.Render(Parse(input, options))
|
return renderer.Render(Parse(input, options))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse is an entry point to the parsing part of Blackfriday. It takes an
|
||||||
|
// input markdown document and produces a syntax tree for its contents. This
|
||||||
|
// tree can then be rendered with a default or custom renderer, or
|
||||||
|
// analyzed/transformed by the caller to whatever non-standard needs they have.
|
||||||
func Parse(input []byte, opts Options) *Node {
|
func Parse(input []byte, opts Options) *Node {
|
||||||
extensions := opts.Extensions
|
extensions := opts.Extensions
|
||||||
|
|
||||||
|
@ -488,7 +499,7 @@ func (p *parser) parseRefsToAST() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.tip = p.doc
|
p.tip = p.doc
|
||||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, []byte(`<div class="footnotes">`)))
|
finalizeHTMLBlock(p.addBlock(HTMLBlock, []byte(`<div class="footnotes">`)))
|
||||||
p.addBlock(HorizontalRule, nil)
|
p.addBlock(HorizontalRule, nil)
|
||||||
block := p.addBlock(List, nil)
|
block := p.addBlock(List, nil)
|
||||||
block.ListFlags = ListTypeOrdered
|
block.ListFlags = ListTypeOrdered
|
||||||
|
@ -514,7 +525,7 @@ func (p *parser) parseRefsToAST() {
|
||||||
above := block.Parent
|
above := block.Parent
|
||||||
finalizeList(block)
|
finalizeList(block)
|
||||||
p.tip = above
|
p.tip = above
|
||||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, []byte("</div>")))
|
finalizeHTMLBlock(p.addBlock(HTMLBlock, []byte("</div>")))
|
||||||
block.Walk(func(node *Node, entering bool) WalkStatus {
|
block.Walk(func(node *Node, entering bool) WalkStatus {
|
||||||
if node.Type == Paragraph || node.Type == Header {
|
if node.Type == Paragraph || node.Type == Header {
|
||||||
p.currBlock = node
|
p.currBlock = node
|
||||||
|
@ -592,7 +603,7 @@ func secondPass(p *parser, input []byte) {
|
||||||
|
|
||||||
if p.flags&Footnotes != 0 && len(p.notes) > 0 {
|
if p.flags&Footnotes != 0 && len(p.notes) > 0 {
|
||||||
flags := ListItemBeginningOfList
|
flags := ListItemBeginningOfList
|
||||||
for i := 0; i < len(p.notes); i += 1 {
|
for i := 0; i < len(p.notes); i++ {
|
||||||
ref := p.notes[i]
|
ref := p.notes[i]
|
||||||
if ref.hasBlock {
|
if ref.hasBlock {
|
||||||
flags |= ListItemContainsBlock
|
flags |= ListItemContainsBlock
|
||||||
|
@ -642,14 +653,14 @@ func secondPass(p *parser, input []byte) {
|
||||||
type reference struct {
|
type reference struct {
|
||||||
link []byte
|
link []byte
|
||||||
title []byte
|
title []byte
|
||||||
noteId int // 0 if not a footnote ref
|
noteID int // 0 if not a footnote ref
|
||||||
hasBlock bool
|
hasBlock bool
|
||||||
text []byte
|
text []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reference) String() string {
|
func (r *reference) String() string {
|
||||||
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteId: %d, hasBlock: %v}",
|
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
|
||||||
r.link, r.title, r.text, r.noteId, r.hasBlock)
|
r.link, r.title, r.text, r.noteID, r.hasBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether or not data starts with a reference link.
|
// Check whether or not data starts with a reference link.
|
||||||
|
@ -667,7 +678,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
noteId := 0
|
noteID := 0
|
||||||
|
|
||||||
// id part: anything but a newline between brackets
|
// id part: anything but a newline between brackets
|
||||||
if data[i] != '[' {
|
if data[i] != '[' {
|
||||||
|
@ -678,7 +689,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||||
if i < len(data) && data[i] == '^' {
|
if i < len(data) && data[i] == '^' {
|
||||||
// we can set it to anything here because the proper noteIds will
|
// we can set it to anything here because the proper noteIds will
|
||||||
// be assigned later during the second pass. It just has to be != 0
|
// be assigned later during the second pass. It just has to be != 0
|
||||||
noteId = 1
|
noteID = 1
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -721,7 +732,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||||
hasBlock bool
|
hasBlock bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if p.flags&Footnotes != 0 && noteId != 0 {
|
if p.flags&Footnotes != 0 && noteID != 0 {
|
||||||
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
|
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
|
||||||
lineEnd = linkEnd
|
lineEnd = linkEnd
|
||||||
} else {
|
} else {
|
||||||
|
@ -734,11 +745,11 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||||
// a valid ref has been found
|
// a valid ref has been found
|
||||||
|
|
||||||
ref := &reference{
|
ref := &reference{
|
||||||
noteId: noteId,
|
noteID: noteID,
|
||||||
hasBlock: hasBlock,
|
hasBlock: hasBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
if noteId > 0 {
|
if noteID > 0 {
|
||||||
// reusing the link field for the id since footnotes don't have links
|
// reusing the link field for the id since footnotes don't have links
|
||||||
ref.link = data[idOffset:idEnd]
|
ref.link = data[idOffset:idEnd]
|
||||||
// if footnote, it's not really a title, it's the contained text
|
// if footnote, it's not really a title, it's the contained text
|
||||||
|
|
52
node.go
52
node.go
|
@ -5,8 +5,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NodeType specifies a type of a single node of a syntax tree. Usually one
|
||||||
|
// node (and its type) corresponds to a single markdown feature, e.g. emphasis
|
||||||
|
// or code block.
|
||||||
type NodeType int
|
type NodeType int
|
||||||
|
|
||||||
|
// Constants for identifying different types of nodes. See NodeType.
|
||||||
const (
|
const (
|
||||||
Document NodeType = iota
|
Document NodeType = iota
|
||||||
BlockQuote
|
BlockQuote
|
||||||
|
@ -65,6 +69,7 @@ func (t NodeType) String() string {
|
||||||
return nodeTypeNames[t]
|
return nodeTypeNames[t]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListData contains fields relevant to a List node type.
|
||||||
type ListData struct {
|
type ListData struct {
|
||||||
ListFlags ListType
|
ListFlags ListType
|
||||||
Tight bool // Skip <p>s around list item data if true
|
Tight bool // Skip <p>s around list item data if true
|
||||||
|
@ -73,12 +78,14 @@ type ListData struct {
|
||||||
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LinkData contains fields relevant to a Link node type.
|
||||||
type LinkData struct {
|
type LinkData struct {
|
||||||
Destination []byte
|
Destination []byte
|
||||||
Title []byte
|
Title []byte
|
||||||
NoteID int
|
NoteID int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CodeBlockData contains fields relevant to a CodeBlock node type.
|
||||||
type CodeBlockData struct {
|
type CodeBlockData struct {
|
||||||
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||||||
Info []byte // This holds the info string
|
Info []byte // This holds the info string
|
||||||
|
@ -87,11 +94,13 @@ type CodeBlockData struct {
|
||||||
FenceOffset int
|
FenceOffset int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableCellData contains fields relevant to a TableCell node type.
|
||||||
type TableCellData struct {
|
type TableCellData struct {
|
||||||
IsHeader bool // This tells if it's under the header row
|
IsHeader bool // This tells if it's under the header row
|
||||||
Align CellAlignFlags // This holds the value for align attribute
|
Align CellAlignFlags // This holds the value for align attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeaderData contains fields relevant to a Header node type.
|
||||||
type HeaderData struct {
|
type HeaderData struct {
|
||||||
Level int // This holds the heading level number
|
Level int // This holds the heading level number
|
||||||
HeaderID string // This might hold header ID, if present
|
HeaderID string // This might hold header ID, if present
|
||||||
|
@ -111,16 +120,17 @@ type Node struct {
|
||||||
|
|
||||||
Literal []byte // Text contents of the leaf nodes
|
Literal []byte // Text contents of the leaf nodes
|
||||||
|
|
||||||
HeaderData // Populated if Type == Header
|
HeaderData // Populated if Type is Header
|
||||||
ListData // Populated if Type == List
|
ListData // Populated if Type is List
|
||||||
CodeBlockData // Populated if Type == CodeBlock
|
CodeBlockData // Populated if Type is CodeBlock
|
||||||
LinkData // Populated if Type == Link
|
LinkData // Populated if Type is Link
|
||||||
TableCellData // Populated if Type == TableCell
|
TableCellData // Populated if Type is TableCell
|
||||||
|
|
||||||
content []byte // Markdown content of the block nodes
|
content []byte // Markdown content of the block nodes
|
||||||
open bool // Specifies an open block node that has not been finished to process yet
|
open bool // Specifies an open block node that has not been finished to process yet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNode allocates a node of a specified type.
|
||||||
func NewNode(typ NodeType) *Node {
|
func NewNode(typ NodeType) *Node {
|
||||||
return &Node{
|
return &Node{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
|
@ -218,7 +228,6 @@ func (n *Node) isContainer() bool {
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) canContain(t NodeType) bool {
|
func (n *Node) canContain(t NodeType) bool {
|
||||||
|
@ -246,9 +255,12 @@ func (n *Node) canContain(t NodeType) bool {
|
||||||
type WalkStatus int
|
type WalkStatus int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GoToNext WalkStatus = iota // The default traversal of every node.
|
// GoToNext is the default traversal of every node.
|
||||||
SkipChildren // Skips all children of current node.
|
GoToNext WalkStatus = iota
|
||||||
Terminate // Terminates the traversal.
|
// SkipChildren tells walker to skip all children of current node.
|
||||||
|
SkipChildren
|
||||||
|
// Terminate tells walker to terminate the traversal.
|
||||||
|
Terminate
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||||||
|
@ -256,8 +268,10 @@ const (
|
||||||
// first visited, then with entering=false after all the children are done.
|
// first visited, then with entering=false after all the children are done.
|
||||||
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
||||||
|
|
||||||
func (root *Node) Walk(visitor NodeVisitor) {
|
// Walk is a convenience method that instantiates a walker and starts a
|
||||||
walker := NewNodeWalker(root)
|
// traversal of subtree rooted at n.
|
||||||
|
func (n *Node) Walk(visitor NodeVisitor) {
|
||||||
|
walker := newNodeWalker(n)
|
||||||
node, entering := walker.next()
|
node, entering := walker.next()
|
||||||
for node != nil {
|
for node != nil {
|
||||||
status := visitor(node, entering)
|
status := visitor(node, entering)
|
||||||
|
@ -272,21 +286,21 @@ func (root *Node) Walk(visitor NodeVisitor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeWalker struct {
|
type nodeWalker struct {
|
||||||
current *Node
|
current *Node
|
||||||
root *Node
|
root *Node
|
||||||
entering bool
|
entering bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodeWalker(root *Node) *NodeWalker {
|
func newNodeWalker(root *Node) *nodeWalker {
|
||||||
return &NodeWalker{
|
return &nodeWalker{
|
||||||
current: root,
|
current: root,
|
||||||
root: nil,
|
root: nil,
|
||||||
entering: true,
|
entering: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nw *NodeWalker) next() (*Node, bool) {
|
func (nw *nodeWalker) next() (*Node, bool) {
|
||||||
if nw.current == nil {
|
if nw.current == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -314,7 +328,7 @@ func (nw *NodeWalker) next() (*Node, bool) {
|
||||||
return nw.current, nw.entering
|
return nw.current, nw.entering
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nw *NodeWalker) resumeAt(node *Node, entering bool) (*Node, bool) {
|
func (nw *nodeWalker) resumeAt(node *Node, entering bool) (*Node, bool) {
|
||||||
nw.current = node
|
nw.current = node
|
||||||
nw.entering = entering
|
nw.entering = entering
|
||||||
return nw.next()
|
return nw.next()
|
||||||
|
@ -324,7 +338,7 @@ func dump(ast *Node) {
|
||||||
fmt.Println(dumpString(ast))
|
fmt.Println(dumpString(ast))
|
||||||
}
|
}
|
||||||
|
|
||||||
func dump_r(ast *Node, depth int) string {
|
func dumpR(ast *Node, depth int) string {
|
||||||
if ast == nil {
|
if ast == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -335,11 +349,11 @@ func dump_r(ast *Node, depth int) string {
|
||||||
}
|
}
|
||||||
result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
|
result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
|
||||||
for n := ast.FirstChild; n != nil; n = n.Next {
|
for n := ast.FirstChild; n != nil; n = n.Next {
|
||||||
result += dump_r(n, depth+1)
|
result += dumpR(n, depth+1)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpString(ast *Node) string {
|
func dumpString(ast *Node) string {
|
||||||
return dump_r(ast, 0)
|
return dumpR(ast, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SPRenderer is a struct containing state of a Smartypants renderer.
|
||||||
type SPRenderer struct {
|
type SPRenderer struct {
|
||||||
inSingleQuote bool
|
inSingleQuote bool
|
||||||
inDoubleQuote bool
|
inDoubleQuote bool
|
||||||
|
@ -108,7 +109,7 @@ func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 2 {
|
if len(text) >= 2 {
|
||||||
t1 := tolower(text[1])
|
t1 := tolower(text[1])
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||||
if len(text) >= 3 {
|
if len(text) >= 3 {
|
||||||
nextChar = text[2]
|
nextChar = text[2]
|
||||||
}
|
}
|
||||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &smrt.inDoubleQuote) {
|
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +143,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||||
if len(text) > 1 {
|
if len(text) > 1 {
|
||||||
nextChar = text[1]
|
nextChar = text[1]
|
||||||
}
|
}
|
||||||
if smartQuoteHelper(out, previousChar, nextChar, 's', &smrt.inSingleQuote) {
|
if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +151,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 3 {
|
if len(text) >= 3 {
|
||||||
t1 := tolower(text[1])
|
t1 := tolower(text[1])
|
||||||
t2 := tolower(text[2])
|
t2 := tolower(text[2])
|
||||||
|
@ -175,7 +176,7 @@ func (smrt *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text [
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 2 {
|
if len(text) >= 2 {
|
||||||
if text[1] == '-' {
|
if text[1] == '-' {
|
||||||
out.WriteString("—")
|
out.WriteString("—")
|
||||||
|
@ -192,7 +193,7 @@ func (smrt *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []b
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 3 && text[1] == '-' && text[2] == '-' {
|
if len(text) >= 3 && text[1] == '-' && text[2] == '-' {
|
||||||
out.WriteString("—")
|
out.WriteString("—")
|
||||||
return 2
|
return 2
|
||||||
|
@ -206,13 +207,13 @@ func (smrt *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, tex
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
||||||
if bytes.HasPrefix(text, []byte(""")) {
|
if bytes.HasPrefix(text, []byte(""")) {
|
||||||
nextChar := byte(0)
|
nextChar := byte(0)
|
||||||
if len(text) >= 7 {
|
if len(text) >= 7 {
|
||||||
nextChar = text[6]
|
nextChar = text[6]
|
||||||
}
|
}
|
||||||
if smartQuoteHelper(out, previousChar, nextChar, quote, &smrt.inDoubleQuote) {
|
if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote) {
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,15 +226,15 @@ func (smrt *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, te
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartAmp(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartAmp(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
return smrt.smartAmpVariant(out, previousChar, text, 'd')
|
return r.smartAmpVariant(out, previousChar, text, 'd')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartAmpAngledQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartAmpAngledQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
return smrt.smartAmpVariant(out, previousChar, text, 'a')
|
return r.smartAmpVariant(out, previousChar, text, 'a')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 3 && text[1] == '.' && text[2] == '.' {
|
if len(text) >= 3 && text[1] == '.' && text[2] == '.' {
|
||||||
out.WriteString("…")
|
out.WriteString("…")
|
||||||
return 2
|
return 2
|
||||||
|
@ -248,13 +249,13 @@ func (smrt *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text [
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if len(text) >= 2 && text[1] == '`' {
|
if len(text) >= 2 && text[1] == '`' {
|
||||||
nextChar := byte(0)
|
nextChar := byte(0)
|
||||||
if len(text) >= 3 {
|
if len(text) >= 3 {
|
||||||
nextChar = text[2]
|
nextChar = text[2]
|
||||||
}
|
}
|
||||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &smrt.inDoubleQuote) {
|
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,7 +264,7 @@ func (smrt *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||||
// is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b
|
// is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b
|
||||||
// note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8)
|
// note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8)
|
||||||
|
@ -305,7 +306,7 @@ func (smrt *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte,
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||||
if text[0] == '1' && text[1] == '/' && text[2] == '2' {
|
if text[0] == '1' && text[1] == '/' && text[2] == '2' {
|
||||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' {
|
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' {
|
||||||
|
@ -333,27 +334,27 @@ func (smrt *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text [
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
||||||
nextChar := byte(0)
|
nextChar := byte(0)
|
||||||
if len(text) > 1 {
|
if len(text) > 1 {
|
||||||
nextChar = text[1]
|
nextChar = text[1]
|
||||||
}
|
}
|
||||||
if !smartQuoteHelper(out, previousChar, nextChar, quote, &smrt.inDoubleQuote) {
|
if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote) {
|
||||||
out.WriteString(""")
|
out.WriteString(""")
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
return smrt.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
return r.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
return smrt.smartDoubleQuoteVariant(out, previousChar, text, 'a')
|
return r.smartDoubleQuoteVariant(out, previousChar, text, 'a')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smrt *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int {
|
func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
for i < len(text) && text[i] != '>' {
|
for i < len(text) && text[i] != '>' {
|
||||||
|
@ -366,6 +367,7 @@ func (smrt *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, tex
|
||||||
|
|
||||||
type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int
|
type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int
|
||||||
|
|
||||||
|
// NewSmartypantsRenderer constructs a Smartypants renderer object.
|
||||||
func NewSmartypantsRenderer(flags Extensions) *SPRenderer {
|
func NewSmartypantsRenderer(flags Extensions) *SPRenderer {
|
||||||
var r SPRenderer
|
var r SPRenderer
|
||||||
if flags&SmartypantsAngledQuotes == 0 {
|
if flags&SmartypantsAngledQuotes == 0 {
|
||||||
|
@ -398,13 +400,14 @@ func NewSmartypantsRenderer(flags Extensions) *SPRenderer {
|
||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr *SPRenderer) Process(text []byte) []byte {
|
// Process is the entry point of the Smartypants renderer.
|
||||||
|
func (r *SPRenderer) Process(text []byte) []byte {
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
// first do normal entity escaping
|
// first do normal entity escaping
|
||||||
text = esc(text)
|
text = esc(text)
|
||||||
mark := 0
|
mark := 0
|
||||||
for i := 0; i < len(text); i++ {
|
for i := 0; i < len(text); i++ {
|
||||||
if action := sr.callbacks[text[i]]; action != nil {
|
if action := r.callbacks[text[i]]; action != nil {
|
||||||
if i > mark {
|
if i > mark {
|
||||||
buff.Write(text[mark:i])
|
buff.Write(text[mark:i])
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user