Rename Markdown()->Run() and Processor->Markdown

This commit is contained in:
Pierre Neidhardt 2017-06-05 21:42:05 +01:00
parent a47518da29
commit 2501229ba6
5 changed files with 83 additions and 83 deletions

View File

@ -34,7 +34,7 @@ var (
// Parse block-level data.
// Note: this function and many that it calls assume that
// the input buffer ends with a newline.
func (p *Processor) block(data []byte) {
func (p *Markdown) block(data []byte) {
// this is called recursively: enforce a maximum depth
if p.nesting >= p.maxNesting {
return
@ -197,14 +197,14 @@ func (p *Processor) block(data []byte) {
p.nesting--
}
func (p *Processor) addBlock(typ NodeType, content []byte) *Node {
func (p *Markdown) addBlock(typ NodeType, content []byte) *Node {
p.closeUnmatchedBlocks()
container := p.addChild(typ, 0)
container.content = content
return container
}
func (p *Processor) isPrefixHeading(data []byte) bool {
func (p *Markdown) isPrefixHeading(data []byte) bool {
if data[0] != '#' {
return false
}
@ -221,7 +221,7 @@ func (p *Processor) isPrefixHeading(data []byte) bool {
return true
}
func (p *Processor) prefixHeading(data []byte) int {
func (p *Markdown) prefixHeading(data []byte) int {
level := 0
for level < 6 && level < len(data) && data[level] == '#' {
level++
@ -267,7 +267,7 @@ func (p *Processor) prefixHeading(data []byte) int {
return skip
}
func (p *Processor) isUnderlinedHeading(data []byte) int {
func (p *Markdown) isUnderlinedHeading(data []byte) int {
// test of level 1 heading
if data[0] == '=' {
i := skipChar(data, 1, '=')
@ -291,7 +291,7 @@ func (p *Processor) isUnderlinedHeading(data []byte) int {
return 0
}
func (p *Processor) titleBlock(data []byte, doRender bool) int {
func (p *Markdown) titleBlock(data []byte, doRender bool) int {
if data[0] != '%' {
return 0
}
@ -315,7 +315,7 @@ func (p *Processor) titleBlock(data []byte, doRender bool) int {
return consumed
}
func (p *Processor) html(data []byte, doRender bool) int {
func (p *Markdown) html(data []byte, doRender bool) int {
var i, j int
// identify the opening tag
@ -419,7 +419,7 @@ func finalizeHTMLBlock(block *Node) {
}
// HTML comment, lax form
func (p *Processor) htmlComment(data []byte, doRender bool) int {
func (p *Markdown) htmlComment(data []byte, doRender bool) int {
i := p.inlineHTMLComment(data)
// needs to end with a blank line
if j := p.isEmpty(data[i:]); j > 0 {
@ -439,7 +439,7 @@ func (p *Processor) htmlComment(data []byte, doRender bool) int {
}
// HR, which is the only self-closing block tag considered
func (p *Processor) htmlHr(data []byte, doRender bool) int {
func (p *Markdown) htmlHr(data []byte, doRender bool) int {
if len(data) < 4 {
return 0
}
@ -472,7 +472,7 @@ func (p *Processor) htmlHr(data []byte, doRender bool) int {
return 0
}
func (p *Processor) htmlFindTag(data []byte) (string, bool) {
func (p *Markdown) htmlFindTag(data []byte) (string, bool) {
i := 0
for i < len(data) && isalnum(data[i]) {
i++
@ -484,7 +484,7 @@ func (p *Processor) htmlFindTag(data []byte) (string, bool) {
return "", false
}
func (p *Processor) htmlFindEnd(tag string, data []byte) int {
func (p *Markdown) htmlFindEnd(tag string, data []byte) int {
// assume data[0] == '<' && data[1] == '/' already tested
if tag == "hr" {
return 2
@ -519,7 +519,7 @@ func (p *Processor) htmlFindEnd(tag string, data []byte) int {
return i + skip
}
func (*Processor) isEmpty(data []byte) int {
func (*Markdown) isEmpty(data []byte) int {
// it is okay to call isEmpty on an empty buffer
if len(data) == 0 {
return 0
@ -537,7 +537,7 @@ func (*Processor) isEmpty(data []byte) int {
return i
}
func (*Processor) isHRule(data []byte) bool {
func (*Markdown) isHRule(data []byte) bool {
i := 0
// skip up to three spaces
@ -667,7 +667,7 @@ func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker
// fencedCodeBlock returns the end index if data contains a fenced code block at the beginning,
// 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 *Processor) fencedCodeBlock(data []byte, doRender bool) int {
func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
var syntax string
beg, marker := isFenceLine(data, &syntax, "")
if beg == 0 || beg >= len(data) {
@ -739,7 +739,7 @@ func finalizeCodeBlock(block *Node) {
block.content = nil
}
func (p *Processor) table(data []byte) int {
func (p *Markdown) table(data []byte) int {
table := p.addBlock(Table, nil)
i, columns := p.tableHeader(data)
if i == 0 {
@ -782,7 +782,7 @@ func isBackslashEscaped(data []byte, i int) bool {
return backslashes&1 == 1
}
func (p *Processor) tableHeader(data []byte) (size int, columns []CellAlignFlags) {
func (p *Markdown) tableHeader(data []byte) (size int, columns []CellAlignFlags) {
i := 0
colCount := 1
for i = 0; i < len(data) && data[i] != '\n'; i++ {
@ -895,7 +895,7 @@ func (p *Processor) tableHeader(data []byte) (size int, columns []CellAlignFlags
return
}
func (p *Processor) tableRow(data []byte, columns []CellAlignFlags, header bool) {
func (p *Markdown) tableRow(data []byte, columns []CellAlignFlags, header bool) {
p.addBlock(TableRow, nil)
i, col := 0, 0
@ -939,7 +939,7 @@ func (p *Processor) tableRow(data []byte, columns []CellAlignFlags, header bool)
}
// returns blockquote prefix length
func (p *Processor) quotePrefix(data []byte) int {
func (p *Markdown) quotePrefix(data []byte) int {
i := 0
for i < 3 && i < len(data) && data[i] == ' ' {
i++
@ -955,7 +955,7 @@ func (p *Processor) quotePrefix(data []byte) int {
// blockquote ends with at least one blank line
// followed by something without a blockquote prefix
func (p *Processor) terminateBlockquote(data []byte, beg, end int) bool {
func (p *Markdown) terminateBlockquote(data []byte, beg, end int) bool {
if p.isEmpty(data[beg:]) <= 0 {
return false
}
@ -966,7 +966,7 @@ func (p *Processor) terminateBlockquote(data []byte, beg, end int) bool {
}
// parse a blockquote fragment
func (p *Processor) quote(data []byte) int {
func (p *Markdown) quote(data []byte) int {
block := p.addBlock(BlockQuote, nil)
var raw bytes.Buffer
beg, end := 0, 0
@ -1004,7 +1004,7 @@ func (p *Processor) quote(data []byte) int {
}
// returns prefix length for block code
func (p *Processor) codePrefix(data []byte) int {
func (p *Markdown) codePrefix(data []byte) int {
if len(data) >= 1 && data[0] == '\t' {
return 1
}
@ -1014,7 +1014,7 @@ func (p *Processor) codePrefix(data []byte) int {
return 0
}
func (p *Processor) code(data []byte) int {
func (p *Markdown) code(data []byte) int {
var work bytes.Buffer
i := 0
@ -1064,7 +1064,7 @@ func (p *Processor) code(data []byte) int {
}
// returns unordered list item prefix
func (p *Processor) uliPrefix(data []byte) int {
func (p *Markdown) uliPrefix(data []byte) int {
i := 0
// start with up to 3 spaces
for i < len(data) && i < 3 && data[i] == ' ' {
@ -1082,7 +1082,7 @@ func (p *Processor) uliPrefix(data []byte) int {
}
// returns ordered list item prefix
func (p *Processor) oliPrefix(data []byte) int {
func (p *Markdown) oliPrefix(data []byte) int {
i := 0
// start with up to 3 spaces
@ -1107,7 +1107,7 @@ func (p *Processor) oliPrefix(data []byte) int {
}
// returns definition list item prefix
func (p *Processor) dliPrefix(data []byte) int {
func (p *Markdown) dliPrefix(data []byte) int {
if len(data) < 2 {
return 0
}
@ -1123,7 +1123,7 @@ func (p *Processor) dliPrefix(data []byte) int {
}
// parse ordered or unordered list block
func (p *Processor) list(data []byte, flags ListType) int {
func (p *Markdown) list(data []byte, flags ListType) int {
i := 0
flags |= ListItemBeginningOfList
block := p.addBlock(List, nil)
@ -1191,7 +1191,7 @@ func finalizeList(block *Node) {
// Parse a single list item.
// Assumes initial prefix is already removed if this is a sublist.
func (p *Processor) listItem(data []byte, flags *ListType) int {
func (p *Markdown) listItem(data []byte, flags *ListType) int {
// keep track of the indentation of the first line
itemIndent := 0
if data[0] == '\t' {
@ -1383,7 +1383,7 @@ gatherlines:
}
// render a single paragraph that has already been parsed out
func (p *Processor) renderParagraph(data []byte) {
func (p *Markdown) renderParagraph(data []byte) {
if len(data) == 0 {
return
}
@ -1408,7 +1408,7 @@ func (p *Processor) renderParagraph(data []byte) {
p.addBlock(Paragraph, data[beg:end])
}
func (p *Processor) paragraph(data []byte) int {
func (p *Markdown) paragraph(data []byte) int {
// prev: index of 1st char of previous line
// line: index of 1st char of current line
// i: index of cursor/end of current line

10
doc.go
View File

@ -7,11 +7,11 @@
// The simplest way to invoke Blackfriday is to call the Markdown function. It
// will take a text input and produce a text output in HTML (or other format).
//
// A slightly more sophisticated way to use Blackfriday is to create a Processor
// and to call Parse, which returns a syntax tree for the input document. You
// can leverage Blackfriday's parsing for content extraction from markdown
// documents. You can assign a custom renderer and set various options to the
// Processor.
// A slightly more sophisticated way to use Blackfriday is to create a Markdown
// processor and to call Parse, which returns a syntax tree for the input
// document. You can leverage Blackfriday's parsing for content extraction from
// markdown documents. You can assign a custom renderer and set various options
// to the Markdown processor.
//
// If you're interested in calling Blackfriday from command line, see
// https://github.com/russross/blackfriday-tool.

View File

@ -46,7 +46,7 @@ func execRecoverableTestSuite(t *testing.T, tests []string, params TestParams, s
func runMarkdown(input string, params TestParams) string {
params.HTMLRendererParameters.Flags = params.HTMLFlags
renderer := NewHTMLRenderer(params.HTMLRendererParameters)
return string(Markdown([]byte(input), WithRenderer(renderer),
return string(Run([]byte(input), WithRenderer(renderer),
WithExtensions(params.extensions),
WithRefOverride(params.referenceOverride)))
}

View File

@ -32,7 +32,7 @@ var (
// data is the complete block being rendered
// offset is the number of valid chars before the current cursor
func (p *Processor) inline(currBlock *Node, data []byte) {
func (p *Markdown) inline(currBlock *Node, data []byte) {
// handlers might call us recursively: enforce a maximum depth
if p.nesting >= p.maxNesting || len(data) == 0 {
return
@ -69,7 +69,7 @@ func (p *Processor) inline(currBlock *Node, data []byte) {
}
// single and double emphasis parsing
func emphasis(p *Processor, data []byte, offset int) (int, *Node) {
func emphasis(p *Markdown, data []byte, offset int) (int, *Node) {
data = data[offset:]
c := data[0]
@ -114,7 +114,7 @@ func emphasis(p *Processor, data []byte, offset int) (int, *Node) {
return 0, nil
}
func codeSpan(p *Processor, data []byte, offset int) (int, *Node) {
func codeSpan(p *Markdown, data []byte, offset int) (int, *Node) {
data = data[offset:]
nb := 0
@ -161,7 +161,7 @@ func codeSpan(p *Processor, data []byte, offset int) (int, *Node) {
}
// newline preceded by two spaces becomes <br>
func maybeLineBreak(p *Processor, data []byte, offset int) (int, *Node) {
func maybeLineBreak(p *Markdown, data []byte, offset int) (int, *Node) {
origOffset := offset
for offset < len(data) && data[offset] == ' ' {
offset++
@ -177,7 +177,7 @@ func maybeLineBreak(p *Processor, data []byte, offset int) (int, *Node) {
}
// newline without two spaces works when HardLineBreak is enabled
func lineBreak(p *Processor, data []byte, offset int) (int, *Node) {
func lineBreak(p *Markdown, data []byte, offset int) (int, *Node) {
if p.extensions&HardLineBreak != 0 {
return 1, NewNode(Hardbreak)
}
@ -200,14 +200,14 @@ func isReferenceStyleLink(data []byte, pos int, t linkType) bool {
return pos < len(data)-1 && data[pos] == '[' && data[pos+1] != '^'
}
func maybeImage(p *Processor, data []byte, offset int) (int, *Node) {
func maybeImage(p *Markdown, data []byte, offset int) (int, *Node) {
if offset < len(data)-1 && data[offset+1] == '[' {
return link(p, data, offset)
}
return 0, nil
}
func maybeInlineFootnote(p *Processor, data []byte, offset int) (int, *Node) {
func maybeInlineFootnote(p *Markdown, data []byte, offset int) (int, *Node) {
if offset < len(data)-1 && data[offset+1] == '[' {
return link(p, data, offset)
}
@ -215,7 +215,7 @@ func maybeInlineFootnote(p *Processor, data []byte, offset int) (int, *Node) {
}
// '[': parse a link or an image or a footnote
func link(p *Processor, data []byte, offset int) (int, *Node) {
func link(p *Markdown, data []byte, offset int) (int, *Node) {
// no links allowed inside regular links, footnote, and deferred footnotes
if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') {
return 0, nil
@ -573,7 +573,7 @@ func link(p *Processor, data []byte, offset int) (int, *Node) {
return i, linkNode
}
func (p *Processor) inlineHTMLComment(data []byte) int {
func (p *Markdown) inlineHTMLComment(data []byte) int {
if len(data) < 5 {
return 0
}
@ -613,7 +613,7 @@ const (
)
// '<' when tags or autolinks are allowed
func leftAngle(p *Processor, data []byte, offset int) (int, *Node) {
func leftAngle(p *Markdown, data []byte, offset int) (int, *Node) {
data = data[offset:]
altype, end := tagLength(data)
if size := p.inlineHTMLComment(data); size > 0 {
@ -646,7 +646,7 @@ func leftAngle(p *Processor, data []byte, offset int) (int, *Node) {
// '\\' backslash escape
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~")
func escape(p *Processor, data []byte, offset int) (int, *Node) {
func escape(p *Markdown, data []byte, offset int) (int, *Node) {
data = data[offset:]
if len(data) > 1 {
@ -686,7 +686,7 @@ func unescapeText(ob *bytes.Buffer, src []byte) {
// '&' escaped when it doesn't belong to an entity
// valid entities are assumed to be anything matching &#?[A-Za-z0-9]+;
func entity(p *Processor, data []byte, offset int) (int, *Node) {
func entity(p *Markdown, data []byte, offset int) (int, *Node) {
data = data[offset:]
end := 1
@ -748,7 +748,7 @@ var protocolPrefixes = [][]byte{
const shortestPrefix = 6 // len("ftp://"), the shortest of the above
func maybeAutoLink(p *Processor, data []byte, offset int) (int, *Node) {
func maybeAutoLink(p *Markdown, data []byte, offset int) (int, *Node) {
// quick check to rule out most false hits
if p.insideLink || len(data) < offset+shortestPrefix {
return 0, nil
@ -765,7 +765,7 @@ func maybeAutoLink(p *Processor, data []byte, offset int) (int, *Node) {
return 0, nil
}
func autoLink(p *Processor, data []byte, offset int) (int, *Node) {
func autoLink(p *Markdown, data []byte, offset int) (int, *Node) {
// Now a more expensive check to see if we're not inside an anchor element
anchorStart := offset
offsetFromAnchor := 0
@ -1095,7 +1095,7 @@ func helperFindEmphChar(data []byte, c byte) int {
return 0
}
func helperEmphasis(p *Processor, data []byte, c byte) (int, *Node) {
func helperEmphasis(p *Markdown, data []byte, c byte) (int, *Node) {
i := 0
// skip one symbol if coming from emph3
@ -1135,7 +1135,7 @@ func helperEmphasis(p *Processor, data []byte, c byte) (int, *Node) {
return 0, nil
}
func helperDoubleEmphasis(p *Processor, data []byte, c byte) (int, *Node) {
func helperDoubleEmphasis(p *Markdown, data []byte, c byte) (int, *Node) {
i := 0
for i < len(data) {
@ -1159,7 +1159,7 @@ func helperDoubleEmphasis(p *Processor, data []byte, c byte) (int, *Node) {
return 0, nil
}
func helperTripleEmphasis(p *Processor, data []byte, offset int, c byte) (int, *Node) {
func helperTripleEmphasis(p *Markdown, data []byte, offset int, c byte) (int, *Node) {
i := 0
origData := data
data = data[offset:]

View File

@ -154,12 +154,12 @@ type Renderer interface {
// Callback functions for inline parsing. One such function is defined
// for each character that triggers a response when parsing inline data.
type inlineParser func(p *Processor, data []byte, offset int) (int, *Node)
type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node)
// Processor holds:
// Markdown is a type that holds:
// - extensions and the runtime state used by Parse,
// - the renderer.
type Processor struct {
type Markdown struct {
renderer Renderer
referenceOverride ReferenceOverrideFunc
refs map[string]*reference
@ -181,7 +181,7 @@ type Processor struct {
allClosed bool
}
func (p *Processor) getRef(refid string) (ref *reference, found bool) {
func (p *Markdown) getRef(refid string) (ref *reference, found bool) {
if p.referenceOverride != nil {
r, overridden := p.referenceOverride(refid)
if overridden {
@ -201,17 +201,17 @@ func (p *Processor) getRef(refid string) (ref *reference, found bool) {
return ref, found
}
func (p *Processor) finalize(block *Node) {
func (p *Markdown) finalize(block *Node) {
above := block.Parent
block.open = false
p.tip = above
}
func (p *Processor) addChild(node NodeType, offset uint32) *Node {
func (p *Markdown) addChild(node NodeType, offset uint32) *Node {
return p.addExistingChild(NewNode(node), offset)
}
func (p *Processor) addExistingChild(node *Node, offset uint32) *Node {
func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node {
for !p.tip.canContain(node.Type) {
p.finalize(p.tip)
}
@ -220,7 +220,7 @@ func (p *Processor) addExistingChild(node *Node, offset uint32) *Node {
return node
}
func (p *Processor) closeUnmatchedBlocks() {
func (p *Markdown) closeUnmatchedBlocks() {
if !p.allClosed {
for p.oldTip != p.lastMatchedContainer {
parent := p.oldTip.Parent
@ -255,10 +255,10 @@ type Reference struct {
// See the documentation in Options for more details on use-case.
type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool)
// NewProcessor constructs a Processor. You can use the same With* functions as
// for Markdown() to customize parser's behavior.
func NewProcessor(opts ...Option) *Processor {
var p Processor
// New constructs a Markdown processor. You can use the same With* functions as
// for Run() to customize parser's behavior and the renderer.
func New(opts ...Option) *Markdown {
var p Markdown
for _, opt := range opts {
opt(&p)
}
@ -300,12 +300,12 @@ func NewProcessor(opts ...Option) *Processor {
return &p
}
// Option customizes Processor's default behavior.
type Option func(*Processor)
// Option customizes the Markdown processor's default behavior.
type Option func(*Markdown)
// WithRenderer allows you to override the default renderer.
func WithRenderer(r Renderer) Option {
return func(p *Processor) {
return func(p *Markdown) {
p.renderer = r
}
}
@ -313,14 +313,14 @@ func WithRenderer(r Renderer) Option {
// WithExtensions allows you to pick some of the many extensions provided by
// Blackfriday. You can bitwise OR them.
func WithExtensions(e Extensions) Option {
return func(p *Processor) {
return func(p *Markdown) {
p.extensions = e
}
}
// WithNoExtensions turns off all extensions and custom behavior.
func WithNoExtensions() Option {
return func(p *Processor) {
return func(p *Markdown) {
p.extensions = NoExtensions
p.renderer = NewHTMLRenderer(HTMLRendererParameters{
Flags: HTMLFlagsNone,
@ -343,37 +343,37 @@ func WithNoExtensions() Option {
// the override function indicates an override did not occur, the refids at
// the bottom will be used to fill in the link details.
func WithRefOverride(o ReferenceOverrideFunc) Option {
return func(p *Processor) {
return func(p *Markdown) {
p.referenceOverride = o
}
}
// Markdown is the main entry point to Blackfriday. It parses and renders a
// Run is the main entry point to Blackfriday. It parses and renders a
// block of markdown-encoded text.
//
// The simplest invocation of Markdown takes one argument, input:
// output := Markdown(input)
// The simplest invocation of Run takes one argument, input:
// output := Run(input)
// This will parse the input with CommonExtensions enabled and render it with
// the default HTMLRenderer (with CommonHTMLFlags).
//
// Variadic arguments opts can customize the default behavior. Since Processor
// Variadic arguments opts can customize the default behavior. Since Markdown
// type does not contain exported fields, you can not use it directly. Instead,
// use the With* functions. For example, this will call the most basic
// functionality, with no extensions:
// output := Markdown(input, WithNoExtensions())
// output := Run(input, WithNoExtensions())
//
// You can use any number of With* arguments, even contradicting ones. They
// will be applied in order of appearance and the latter will override the
// former:
// output := Markdown(input, WithNoExtensions(), WithExtensions(exts),
// output := Run(input, WithNoExtensions(), WithExtensions(exts),
// WithRenderer(yourRenderer))
func Markdown(input []byte, opts ...Option) []byte {
func Run(input []byte, opts ...Option) []byte {
r := NewHTMLRenderer(HTMLRendererParameters{
Flags: CommonHTMLFlags,
})
optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)}
optList = append(optList, opts...)
parser := NewProcessor(optList...)
parser := New(optList...)
return parser.renderer.Render(parser.Parse(input))
}
@ -381,7 +381,7 @@ func Markdown(input []byte, opts ...Option) []byte {
// 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 (p *Processor) Parse(input []byte) *Node {
func (p *Markdown) Parse(input []byte) *Node {
p.block(input)
// Walk the tree and finish up some of unfinished blocks
for p.tip != nil {
@ -399,7 +399,7 @@ func (p *Processor) Parse(input []byte) *Node {
return p.doc
}
func (p *Processor) parseRefsToAST() {
func (p *Markdown) parseRefsToAST() {
if p.extensions&Footnotes == 0 || len(p.notes) == 0 {
return
}
@ -524,7 +524,7 @@ func (r *reference) String() string {
// (in the render struct).
// Returns the number of bytes to skip to move past it,
// or zero if the first line is not a reference.
func isReference(p *Processor, data []byte, tabSize int) int {
func isReference(p *Markdown, data []byte, tabSize int) int {
// up to 3 optional leading spaces
if len(data) < 4 {
return 0
@ -627,7 +627,7 @@ func isReference(p *Processor, data []byte, tabSize int) int {
return lineEnd
}
func scanLinkRef(p *Processor, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
// link: whitespace-free sequence, optionally between angle brackets
if data[i] == '<' {
i++
@ -701,7 +701,7 @@ func scanLinkRef(p *Processor, data []byte, i int) (linkOffset, linkEnd, titleOf
// blockEnd is the end of the section in the input buffer, and contents is the
// extracted text that was shifted over one tab. It will need to be rendered at
// the end of the document.
func scanFootnote(p *Processor, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
if i == 0 || len(data) == 0 {
return
}