mirror of
https://github.com/danog/blackfriday.git
synced 2024-11-30 04:29:13 +01: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 (
|
||||
Entity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
|
||||
Escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
|
||||
charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
|
||||
escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
|
||||
)
|
||||
|
||||
var (
|
||||
reBackslashOrAmp = regexp.MustCompile("[\\&]")
|
||||
reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + Escapable + "|" + Entity)
|
||||
reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity)
|
||||
reTrailingWhitespace = regexp.MustCompile("(\n *)+$")
|
||||
)
|
||||
|
||||
@ -279,9 +279,8 @@ func (p *parser) isUnderlinedHeader(data []byte) int {
|
||||
i = skipChar(data, i, ' ')
|
||||
if data[i] == '\n' {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// test of level 2 header
|
||||
@ -290,9 +289,8 @@ func (p *parser) isUnderlinedHeader(data []byte) int {
|
||||
i = skipChar(data, i, ' ')
|
||||
if data[i] == '\n' {
|
||||
return 2
|
||||
} else {
|
||||
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' {
|
||||
end--
|
||||
}
|
||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||
finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func finalizeHtmlBlock(block *Node) {
|
||||
func finalizeHTMLBlock(block *Node) {
|
||||
block.Literal = reTrailingWhitespace.ReplaceAll(block.content, []byte{})
|
||||
block.content = []byte{}
|
||||
}
|
||||
|
||||
// HTML comment, lax form
|
||||
func (p *parser) htmlComment(data []byte, doRender bool) int {
|
||||
i := p.inlineHtmlComment(data)
|
||||
i := p.inlineHTMLComment(data)
|
||||
// needs to end with a blank line
|
||||
if j := p.isEmpty(data[i:]); j > 0 {
|
||||
size := i + j
|
||||
@ -438,7 +436,7 @@ func (p *parser) htmlComment(data []byte, doRender bool) int {
|
||||
end--
|
||||
}
|
||||
block := p.addBlock(HTMLBlock, data[:end])
|
||||
finalizeHtmlBlock(block)
|
||||
finalizeHTMLBlock(block)
|
||||
}
|
||||
return size
|
||||
}
|
||||
@ -470,7 +468,7 @@ func (p *parser) htmlHr(data []byte, doRender bool) int {
|
||||
for end > 0 && data[end-1] == '\n' {
|
||||
end--
|
||||
}
|
||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||
finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
|
||||
}
|
||||
return size
|
||||
}
|
||||
@ -729,9 +727,8 @@ func unescapeChar(str []byte) []byte {
|
||||
func unescapeString(str []byte) []byte {
|
||||
if reBackslashOrAmp.Match(str) {
|
||||
return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar)
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func finalizeCodeBlock(block *Node) {
|
||||
|
@ -1572,7 +1572,7 @@ func TestCompletePage(t *testing.T) {
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title></title>
|
||||
<meta name="GENERATOR" content="Blackfriday Markdown Processor v1.4" />
|
||||
<meta name="GENERATOR" content="Blackfriday Markdown Processor v2.0" />
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -54,7 +54,7 @@ func doTests(t *testing.T, tests []string) {
|
||||
doTestsParam(t, tests, TestParams{
|
||||
Options: DefaultOptions,
|
||||
HTMLRendererParameters: HTMLRendererParameters{
|
||||
Flags: CommonHtmlFlags,
|
||||
Flags: CommonHTMLFlags,
|
||||
Extensions: CommonExtensions,
|
||||
},
|
||||
})
|
||||
@ -106,7 +106,7 @@ func doLinkTestsInline(t *testing.T, tests []string) {
|
||||
HTMLRendererParameters: params,
|
||||
})
|
||||
doTestsInlineParam(t, transformTests, TestParams{
|
||||
HTMLFlags: CommonHtmlFlags,
|
||||
HTMLFlags: CommonHTMLFlags,
|
||||
HTMLRendererParameters: params,
|
||||
})
|
||||
}
|
||||
|
24
html.go
24
html.go
@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTMLFlags control optional behavior of HTML renderer.
|
||||
type HTMLFlags int
|
||||
|
||||
// HTML renderer configuration options.
|
||||
@ -63,6 +64,8 @@ var (
|
||||
htmlTagRe = regexp.MustCompile("(?i)^" + HTMLTag)
|
||||
)
|
||||
|
||||
// HTMLRendererParameters is a collection of supplementary parameters tweaking
|
||||
// the behavior of various parts of HTML renderer.
|
||||
type HTMLRendererParameters struct {
|
||||
// Prepend this text to each relative URL.
|
||||
AbsolutePrefix string
|
||||
@ -126,8 +129,8 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
func isHtmlTag(tag []byte, tagname string) bool {
|
||||
found, _ := findHtmlTagPos(tag, tagname)
|
||||
func isHTMLTag(tag []byte, tagname string) bool {
|
||||
found, _ := findHTMLTagPos(tag, tagname)
|
||||
return found
|
||||
}
|
||||
|
||||
@ -154,7 +157,7 @@ func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
|
||||
return start
|
||||
}
|
||||
|
||||
func findHtmlTagPos(tag []byte, tagname string) (bool, int) {
|
||||
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
|
||||
i := 0
|
||||
if i < len(tag) && tag[0] != '<' {
|
||||
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 {
|
||||
attrs := []string{}
|
||||
switch node.Type {
|
||||
@ -420,7 +433,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||
if r.Flags&SkipHTML != 0 {
|
||||
break
|
||||
}
|
||||
if r.Flags&SkipStyle != 0 && isHtmlTag(node.Literal, "style") {
|
||||
if r.Flags&SkipStyle != 0 && isHTMLTag(node.Literal, "style") {
|
||||
break
|
||||
}
|
||||
//if options.safe {
|
||||
@ -714,7 +727,7 @@ func (r *HTMLRenderer) writeDocumentHeader(w *bytes.Buffer, sr *SPRenderer) {
|
||||
}
|
||||
w.WriteString("</title>\n")
|
||||
w.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
||||
w.WriteString(VERSION)
|
||||
w.WriteString(Version)
|
||||
w.WriteString("\"")
|
||||
w.WriteString(ending)
|
||||
w.WriteString(">\n")
|
||||
@ -739,6 +752,7 @@ func (r *HTMLRenderer) writeDocumentFooter(w *bytes.Buffer) {
|
||||
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 {
|
||||
//println("render_Blackfriday")
|
||||
//dump(ast)
|
||||
|
36
inline.go
36
inline.go
@ -266,13 +266,13 @@ func link(p *parser, data []byte, offset int) int {
|
||||
// ![alt] == image
|
||||
case offset >= 0 && data[offset] == '!':
|
||||
t = linkImg
|
||||
offset += 1
|
||||
offset++
|
||||
// ^[text] == inline footnote
|
||||
// [^refId] == deferred footnote
|
||||
case p.flags&Footnotes != 0:
|
||||
if offset >= 0 && data[offset] == '^' {
|
||||
t = linkInlineFootnote
|
||||
offset += 1
|
||||
offset++
|
||||
} else if len(data)-1 > offset && data[offset+1] == '^' {
|
||||
t = linkDeferredFootnote
|
||||
}
|
||||
@ -285,7 +285,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||
|
||||
var (
|
||||
i = 1
|
||||
noteId int
|
||||
noteID int
|
||||
title, link, altContent []byte
|
||||
textHasNl = false
|
||||
)
|
||||
@ -501,7 +501,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||
|
||||
if t == linkInlineFootnote {
|
||||
// create a new reference
|
||||
noteId = len(p.notes) + 1
|
||||
noteID = len(p.notes) + 1
|
||||
|
||||
var fragment []byte
|
||||
if len(id) > 0 {
|
||||
@ -512,11 +512,11 @@ func link(p *parser, data []byte, offset int) int {
|
||||
}
|
||||
copy(fragment, slugify(id))
|
||||
} else {
|
||||
fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteId))...)
|
||||
fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteID))...)
|
||||
}
|
||||
|
||||
ref := &reference{
|
||||
noteId: noteId,
|
||||
noteID: noteID,
|
||||
hasBlock: false,
|
||||
link: fragment,
|
||||
title: id,
|
||||
@ -534,7 +534,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||
}
|
||||
|
||||
if t == linkDeferredFootnote {
|
||||
lr.noteId = len(p.notes) + 1
|
||||
lr.noteID = len(p.notes) + 1
|
||||
p.notes = append(p.notes, lr)
|
||||
}
|
||||
|
||||
@ -542,7 +542,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||
link = lr.link
|
||||
// if inline footnote, title == footnote contents
|
||||
title = lr.title
|
||||
noteId = lr.noteId
|
||||
noteID = lr.noteID
|
||||
}
|
||||
|
||||
// rewind the whitespace
|
||||
@ -590,13 +590,13 @@ func link(p *parser, data []byte, offset int) int {
|
||||
linkNode.Title = title
|
||||
p.currBlock.appendChild(linkNode)
|
||||
linkNode.appendChild(text(data[1:txtE]))
|
||||
i += 1
|
||||
i++
|
||||
|
||||
case linkInlineFootnote, linkDeferredFootnote:
|
||||
linkNode := NewNode(Link)
|
||||
linkNode.Destination = link
|
||||
linkNode.Title = title
|
||||
linkNode.NoteID = noteId
|
||||
linkNode.NoteID = noteID
|
||||
p.currBlock.appendChild(linkNode)
|
||||
if t == linkInlineFootnote {
|
||||
i++
|
||||
@ -609,7 +609,7 @@ func link(p *parser, data []byte, offset int) int {
|
||||
return i
|
||||
}
|
||||
|
||||
func (p *parser) inlineHtmlComment(data []byte) int {
|
||||
func (p *parser) inlineHTMLComment(data []byte) int {
|
||||
if len(data) < 5 {
|
||||
return 0
|
||||
}
|
||||
@ -643,7 +643,7 @@ func leftAngle(p *parser, data []byte, offset int) int {
|
||||
data = data[offset:]
|
||||
altype := LinkTypeNotAutolink
|
||||
end := tagLength(data, &altype)
|
||||
if size := p.inlineHtmlComment(data); size > 0 {
|
||||
if size := p.inlineHTMLComment(data); size > 0 {
|
||||
end = size
|
||||
}
|
||||
if end > 2 {
|
||||
@ -1026,9 +1026,8 @@ func isMailtoAutoLink(data []byte) int {
|
||||
case '>':
|
||||
if nb == 1 {
|
||||
return i + 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@ -1091,9 +1090,8 @@ func helperFindEmphChar(data []byte, c byte) int {
|
||||
if data[i] != '[' && data[i] != '(' { // not a link
|
||||
if tmpI > 0 {
|
||||
return tmpI
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
cc := data[i]
|
||||
i++
|
||||
@ -1218,17 +1216,15 @@ func helperTripleEmphasis(p *parser, data []byte, offset int, c byte) int {
|
||||
length = helperEmphasis(p, origData[offset-2:], c)
|
||||
if length == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return length - 2
|
||||
}
|
||||
return length - 2
|
||||
default:
|
||||
// single symbol found, hand over to emph2
|
||||
length = helperDoubleEmphasis(p, origData[offset-1:], c)
|
||||
if length == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return length - 1
|
||||
}
|
||||
return length - 1
|
||||
}
|
||||
}
|
||||
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(" breaklinks=true,%\n")
|
||||
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("\\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.
|
||||
package blackfriday
|
||||
@ -26,8 +26,11 @@ import (
|
||||
"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
|
||||
|
||||
// These are the supported markdown parsing extensions.
|
||||
@ -58,7 +61,7 @@ const (
|
||||
TOC // Generate a 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 |
|
||||
Autolink | Strikethrough | SpaceHeaders | HeaderIDs |
|
||||
@ -66,10 +69,13 @@ const (
|
||||
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
|
||||
)
|
||||
|
||||
// DefaultOptions is a convenience variable with all the options that are
|
||||
// enabled by default.
|
||||
var DefaultOptions = Options{
|
||||
Extensions: CommonExtensions,
|
||||
}
|
||||
|
||||
// TODO: this should probably be unexported. Or moved to node.go
|
||||
type LinkType int
|
||||
|
||||
// These are the possible flag values for the link renderer.
|
||||
@ -81,6 +87,7 @@ const (
|
||||
LinkTypeEmail
|
||||
)
|
||||
|
||||
// ListType contains bitwise or'ed flags for list and list item objects.
|
||||
type ListType int
|
||||
|
||||
// These are the possible flag values for the ListItem renderer.
|
||||
@ -96,6 +103,7 @@ const (
|
||||
ListItemEndOfList
|
||||
)
|
||||
|
||||
// CellAlignFlags holds a type of alignment in a table cell.
|
||||
type CellAlignFlags int
|
||||
|
||||
// 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{
|
||||
link: []byte(r.Link),
|
||||
title: []byte(r.Title),
|
||||
noteId: 0,
|
||||
noteID: 0,
|
||||
hasBlock: false,
|
||||
text: []byte(r.Text)}, true
|
||||
}
|
||||
@ -312,9 +320,8 @@ func MarkdownBasic(input []byte) []byte {
|
||||
return Markdown(input, renderer, Options{})
|
||||
}
|
||||
|
||||
// Call Markdown with most useful extensions enabled
|
||||
// MarkdownCommon is a convenience function for simple rendering.
|
||||
// It processes markdown input with common extensions enabled, including:
|
||||
// MarkdownCommon is a convenience function for simple rendering. It calls
|
||||
// Markdown with most useful extensions enabled, including:
|
||||
//
|
||||
// * Smartypants processing with smart fractions and LaTeX dashes
|
||||
//
|
||||
@ -334,7 +341,7 @@ func MarkdownBasic(input []byte) []byte {
|
||||
func MarkdownCommon(input []byte) []byte {
|
||||
// set up the HTML renderer
|
||||
renderer := NewHTMLRenderer(HTMLRendererParameters{
|
||||
Flags: CommonHtmlFlags,
|
||||
Flags: CommonHTMLFlags,
|
||||
Extensions: CommonExtensions,
|
||||
})
|
||||
return Markdown(input, renderer, DefaultOptions)
|
||||
@ -354,6 +361,10 @@ func Markdown(input []byte, renderer Renderer, options Options) []byte {
|
||||
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 {
|
||||
extensions := opts.Extensions
|
||||
|
||||
@ -488,7 +499,7 @@ func (p *parser) parseRefsToAST() {
|
||||
return
|
||||
}
|
||||
p.tip = p.doc
|
||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, []byte(`<div class="footnotes">`)))
|
||||
finalizeHTMLBlock(p.addBlock(HTMLBlock, []byte(`<div class="footnotes">`)))
|
||||
p.addBlock(HorizontalRule, nil)
|
||||
block := p.addBlock(List, nil)
|
||||
block.ListFlags = ListTypeOrdered
|
||||
@ -514,7 +525,7 @@ func (p *parser) parseRefsToAST() {
|
||||
above := block.Parent
|
||||
finalizeList(block)
|
||||
p.tip = above
|
||||
finalizeHtmlBlock(p.addBlock(HTMLBlock, []byte("</div>")))
|
||||
finalizeHTMLBlock(p.addBlock(HTMLBlock, []byte("</div>")))
|
||||
block.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
if node.Type == Paragraph || node.Type == Header {
|
||||
p.currBlock = node
|
||||
@ -592,7 +603,7 @@ func secondPass(p *parser, input []byte) {
|
||||
|
||||
if p.flags&Footnotes != 0 && len(p.notes) > 0 {
|
||||
flags := ListItemBeginningOfList
|
||||
for i := 0; i < len(p.notes); i += 1 {
|
||||
for i := 0; i < len(p.notes); i++ {
|
||||
ref := p.notes[i]
|
||||
if ref.hasBlock {
|
||||
flags |= ListItemContainsBlock
|
||||
@ -642,14 +653,14 @@ func secondPass(p *parser, input []byte) {
|
||||
type reference struct {
|
||||
link []byte
|
||||
title []byte
|
||||
noteId int // 0 if not a footnote ref
|
||||
noteID int // 0 if not a footnote ref
|
||||
hasBlock bool
|
||||
text []byte
|
||||
}
|
||||
|
||||
func (r *reference) String() string {
|
||||
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteId: %d, hasBlock: %v}",
|
||||
r.link, r.title, r.text, r.noteId, r.hasBlock)
|
||||
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
|
||||
r.link, r.title, r.text, r.noteID, r.hasBlock)
|
||||
}
|
||||
|
||||
// Check whether or not data starts with a reference link.
|
||||
@ -667,7 +678,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||
i++
|
||||
}
|
||||
|
||||
noteId := 0
|
||||
noteID := 0
|
||||
|
||||
// id part: anything but a newline between brackets
|
||||
if data[i] != '[' {
|
||||
@ -678,7 +689,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||
if i < len(data) && data[i] == '^' {
|
||||
// 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
|
||||
noteId = 1
|
||||
noteID = 1
|
||||
i++
|
||||
}
|
||||
}
|
||||
@ -721,7 +732,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||
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)
|
||||
lineEnd = linkEnd
|
||||
} else {
|
||||
@ -734,11 +745,11 @@ func isReference(p *parser, data []byte, tabSize int) int {
|
||||
// a valid ref has been found
|
||||
|
||||
ref := &reference{
|
||||
noteId: noteId,
|
||||
noteID: noteID,
|
||||
hasBlock: hasBlock,
|
||||
}
|
||||
|
||||
if noteId > 0 {
|
||||
if noteID > 0 {
|
||||
// reusing the link field for the id since footnotes don't have links
|
||||
ref.link = data[idOffset:idEnd]
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// Constants for identifying different types of nodes. See NodeType.
|
||||
const (
|
||||
Document NodeType = iota
|
||||
BlockQuote
|
||||
@ -65,6 +69,7 @@ func (t NodeType) String() string {
|
||||
return nodeTypeNames[t]
|
||||
}
|
||||
|
||||
// ListData contains fields relevant to a List node type.
|
||||
type ListData struct {
|
||||
ListFlags ListType
|
||||
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
|
||||
}
|
||||
|
||||
// LinkData contains fields relevant to a Link node type.
|
||||
type LinkData struct {
|
||||
Destination []byte
|
||||
Title []byte
|
||||
NoteID int
|
||||
}
|
||||
|
||||
// CodeBlockData contains fields relevant to a CodeBlock node type.
|
||||
type CodeBlockData struct {
|
||||
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||||
Info []byte // This holds the info string
|
||||
@ -87,11 +94,13 @@ type CodeBlockData struct {
|
||||
FenceOffset int
|
||||
}
|
||||
|
||||
// TableCellData contains fields relevant to a TableCell node type.
|
||||
type TableCellData struct {
|
||||
IsHeader bool // This tells if it's under the header row
|
||||
Align CellAlignFlags // This holds the value for align attribute
|
||||
}
|
||||
|
||||
// HeaderData contains fields relevant to a Header node type.
|
||||
type HeaderData struct {
|
||||
Level int // This holds the heading level number
|
||||
HeaderID string // This might hold header ID, if present
|
||||
@ -111,16 +120,17 @@ type Node struct {
|
||||
|
||||
Literal []byte // Text contents of the leaf nodes
|
||||
|
||||
HeaderData // Populated if Type == Header
|
||||
ListData // Populated if Type == List
|
||||
CodeBlockData // Populated if Type == CodeBlock
|
||||
LinkData // Populated if Type == Link
|
||||
TableCellData // Populated if Type == TableCell
|
||||
HeaderData // Populated if Type is Header
|
||||
ListData // Populated if Type is List
|
||||
CodeBlockData // Populated if Type is CodeBlock
|
||||
LinkData // Populated if Type is Link
|
||||
TableCellData // Populated if Type is TableCell
|
||||
|
||||
content []byte // Markdown content of the block nodes
|
||||
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 {
|
||||
return &Node{
|
||||
Type: typ,
|
||||
@ -218,7 +228,6 @@ func (n *Node) isContainer() bool {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *Node) canContain(t NodeType) bool {
|
||||
@ -246,9 +255,12 @@ func (n *Node) canContain(t NodeType) bool {
|
||||
type WalkStatus int
|
||||
|
||||
const (
|
||||
GoToNext WalkStatus = iota // The default traversal of every node.
|
||||
SkipChildren // Skips all children of current node.
|
||||
Terminate // Terminates the traversal.
|
||||
// GoToNext is the default traversal of every node.
|
||||
GoToNext WalkStatus = iota
|
||||
// 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.
|
||||
@ -256,8 +268,10 @@ const (
|
||||
// first visited, then with entering=false after all the children are done.
|
||||
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
||||
|
||||
func (root *Node) Walk(visitor NodeVisitor) {
|
||||
walker := NewNodeWalker(root)
|
||||
// Walk is a convenience method that instantiates a walker and starts a
|
||||
// traversal of subtree rooted at n.
|
||||
func (n *Node) Walk(visitor NodeVisitor) {
|
||||
walker := newNodeWalker(n)
|
||||
node, entering := walker.next()
|
||||
for node != nil {
|
||||
status := visitor(node, entering)
|
||||
@ -272,21 +286,21 @@ func (root *Node) Walk(visitor NodeVisitor) {
|
||||
}
|
||||
}
|
||||
|
||||
type NodeWalker struct {
|
||||
type nodeWalker struct {
|
||||
current *Node
|
||||
root *Node
|
||||
entering bool
|
||||
}
|
||||
|
||||
func NewNodeWalker(root *Node) *NodeWalker {
|
||||
return &NodeWalker{
|
||||
func newNodeWalker(root *Node) *nodeWalker {
|
||||
return &nodeWalker{
|
||||
current: root,
|
||||
root: nil,
|
||||
entering: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (nw *NodeWalker) next() (*Node, bool) {
|
||||
func (nw *nodeWalker) next() (*Node, bool) {
|
||||
if nw.current == nil {
|
||||
return nil, false
|
||||
}
|
||||
@ -314,7 +328,7 @@ func (nw *NodeWalker) next() (*Node, bool) {
|
||||
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.entering = entering
|
||||
return nw.next()
|
||||
@ -324,7 +338,7 @@ func dump(ast *Node) {
|
||||
fmt.Println(dumpString(ast))
|
||||
}
|
||||
|
||||
func dump_r(ast *Node, depth int) string {
|
||||
func dumpR(ast *Node, depth int) string {
|
||||
if ast == nil {
|
||||
return ""
|
||||
}
|
||||
@ -335,11 +349,11 @@ func dump_r(ast *Node, depth int) string {
|
||||
}
|
||||
result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
|
||||
for n := ast.FirstChild; n != nil; n = n.Next {
|
||||
result += dump_r(n, depth+1)
|
||||
result += dumpR(n, depth+1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func dumpString(ast *Node) string {
|
||||
return dump_r(ast, 0)
|
||||
return dumpR(ast, 0)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// SPRenderer is a struct containing state of a Smartypants renderer.
|
||||
type SPRenderer struct {
|
||||
inSingleQuote bool
|
||||
inDoubleQuote bool
|
||||
@ -108,7 +109,7 @@ func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote
|
||||
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 {
|
||||
t1 := tolower(text[1])
|
||||
|
||||
@ -117,7 +118,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &smrt.inDoubleQuote) {
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@ -142,7 +143,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 's', &smrt.inSingleQuote) {
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote) {
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -150,7 +151,7 @@ func (smrt *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, t
|
||||
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 {
|
||||
t1 := tolower(text[1])
|
||||
t2 := tolower(text[2])
|
||||
@ -175,7 +176,7 @@ func (smrt *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text [
|
||||
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 text[1] == '-' {
|
||||
out.WriteString("—")
|
||||
@ -192,7 +193,7 @@ func (smrt *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []b
|
||||
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] == '-' {
|
||||
out.WriteString("—")
|
||||
return 2
|
||||
@ -206,13 +207,13 @@ func (smrt *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, tex
|
||||
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(""")) {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 7 {
|
||||
nextChar = text[6]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, quote, &smrt.inDoubleQuote) {
|
||||
if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote) {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
@ -225,15 +226,15 @@ func (smrt *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, te
|
||||
return 0
|
||||
}
|
||||
|
||||
func (smrt *SPRenderer) smartAmp(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return smrt.smartAmpVariant(out, previousChar, text, 'd')
|
||||
func (r *SPRenderer) smartAmp(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartAmpVariant(out, previousChar, text, 'd')
|
||||
}
|
||||
|
||||
func (smrt *SPRenderer) smartAmpAngledQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return smrt.smartAmpVariant(out, previousChar, text, 'a')
|
||||
func (r *SPRenderer) smartAmpAngledQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
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] == '.' {
|
||||
out.WriteString("…")
|
||||
return 2
|
||||
@ -248,13 +249,13 @@ func (smrt *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text [
|
||||
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] == '`' {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &smrt.inDoubleQuote) {
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@ -263,7 +264,7 @@ func (smrt *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text
|
||||
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 {
|
||||
// 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)
|
||||
@ -305,7 +306,7 @@ func (smrt *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte,
|
||||
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 text[0] == '1' && text[1] == '/' && text[2] == '2' {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if !smartQuoteHelper(out, previousChar, nextChar, quote, &smrt.inDoubleQuote) {
|
||||
if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote) {
|
||||
out.WriteString(""")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (smrt *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return smrt.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
||||
func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
||||
}
|
||||
|
||||
func (smrt *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return smrt.smartDoubleQuoteVariant(out, previousChar, text, 'a')
|
||||
func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// NewSmartypantsRenderer constructs a Smartypants renderer object.
|
||||
func NewSmartypantsRenderer(flags Extensions) *SPRenderer {
|
||||
var r SPRenderer
|
||||
if flags&SmartypantsAngledQuotes == 0 {
|
||||
@ -398,13 +400,14 @@ func NewSmartypantsRenderer(flags Extensions) *SPRenderer {
|
||||
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
|
||||
// first do normal entity escaping
|
||||
text = esc(text)
|
||||
mark := 0
|
||||
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 {
|
||||
buff.Write(text[mark:i])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user