Implementation and some tests for inline footnotes. Also I noticed the list items had the wrong ids, that was silly of me.

This commit is contained in:
moshee 2013-07-01 01:37:52 +00:00
parent 8346559a1a
commit c23099e5ee
4 changed files with 99 additions and 23 deletions

View File

@ -333,7 +333,7 @@ func (options *Html) FootnoteItem(out *bytes.Buffer, name, text []byte, flags in
if flags&LIST_ITEM_CONTAINS_BLOCK != 0 || flags&LIST_ITEM_BEGINNING_OF_LIST != 0 {
doubleSpace(out)
}
out.WriteString(`<li class="`)
out.WriteString(`<li id="fn:`)
out.Write(slugify(name))
out.WriteString(`">`)
out.Write(text)

View File

@ -15,6 +15,7 @@ package blackfriday
import (
"bytes"
"strconv"
)
// Functions to parse text within a block
@ -174,8 +175,7 @@ const (
linkNormal linkType = iota
linkImg
linkDeferredFootnote
// linkInlineFootnote
linkInlineFootnote
)
// '[': parse a link or an image or a footnote
@ -193,7 +193,9 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
if offset > 0 && data[offset-1] == '!' {
t = linkImg
} else if p.flags&EXTENSION_FOOTNOTES != 0 {
if len(data)-1 > offset && data[offset+1] == '^' {
if offset > 0 && data[offset-1] == '^' {
t = linkInlineFootnote
} else if len(data)-1 > offset && data[offset+1] == '^' {
t = linkDeferredFootnote
}
}
@ -387,7 +389,7 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
title = lr.title
i++
// shortcut reference style link or footnote
// shortcut reference style link or reference or inline footnote
default:
var id []byte
@ -407,24 +409,58 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
id = b.Bytes()
} else {
if t == linkDeferredFootnote {
id = data[2:txtE]
id = data[2:txtE] // get rid of the ^
} else {
id = data[1:txtE]
}
}
// find the reference with matching id
key := string(bytes.ToLower(id))
lr, ok := p.refs[key]
if !ok {
return 0
}
if t == linkInlineFootnote {
// create a new reference
noteId = len(p.notes) + 1
// keep link and title from reference
link = lr.link
// if inline footnote, title == footnote contents
title = lr.title
noteId = lr.noteId
var fragment []byte
if len(id) > 0 {
if len(id) < 16 {
fragment = make([]byte, len(id))
} else {
fragment = make([]byte, 16)
}
copy(fragment, slugify(id))
} else {
fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteId))...)
}
ref := &reference{
noteId: noteId,
hasBlock: false,
link: fragment,
title: id,
}
p.notes = append(p.notes, ref)
link = ref.link
title = ref.title
} else {
// find the reference with matching id
lr, ok := p.refs[key]
if !ok {
return 0
}
if t == linkDeferredFootnote {
lr.noteId = len(p.notes) + 1
p.notes = append(p.notes, lr)
}
// keep link and title from reference
link = lr.link
// if inline footnote, title == footnote contents
title = lr.title
noteId = lr.noteId
}
// rewind the whitespace
i = txtE + 1
@ -470,6 +506,15 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
p.r.Image(out, uLink, title, content.Bytes())
case linkInlineFootnote:
outSize := out.Len()
outBytes := out.Bytes()
if outSize > 0 && outBytes[outSize-1] == '^' {
out.Truncate(outSize - 1)
}
p.r.FootnoteRef(out, link, noteId)
case linkDeferredFootnote:
p.r.FootnoteRef(out, link, noteId)

View File

@ -513,7 +513,7 @@ func TestFootnotes(t *testing.T) {
<hr />
<ol>
<li class="a">This is the note
<li id="fn:a">This is the note
</li>
</ol>
</div>
@ -539,7 +539,7 @@ No longer in the footnote
<hr />
<ol>
<li class="b"><p>Paragraph 1</p>
<li id="fn:b"><p>Paragraph 1</p>
<p>Paragraph 2</p>
@ -577,9 +577,39 @@ what happens here
<hr />
<ol>
<li class="c">this is <a href="/link/c">note</a> c
<li id="fn:c">this is <a href="/link/c">note</a> c
</li>
<li class="d">this is note d
<li id="fn:d">this is note d
</li>
</ol>
</div>
`,
"testing inline^[this is the note] notes.\n",
`<p>testing inline<sup class="footnote-ref" id="fnref:this-is-the-note"><a rel="footnote" href="#fn:this-is-the-note">1</a></sup> notes.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:this-is-the-note">this is the note</li>
</ol>
</div>
`,
"testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n",
`<p>testing multiple<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup> types<sup class="footnote-ref" id="fnref:inline-note"><a rel="footnote" href="#fn:inline-note">2</a></sup> of notes<sup class="footnote-ref" id="fnref:2"><a rel="footnote" href="#fn:2">3</a></sup></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1"><p>the first deferred note</p>
<p>which happens to be a block</p>
</li>
<li id="fn:inline-note">inline note</li>
<li id="fn:2">the second deferred note
</li>
</ol>
</div>

View File

@ -436,7 +436,9 @@ func isReference(p *parser, data []byte, tabSize int) int {
i++
if p.flags&EXTENSION_FOOTNOTES != 0 {
if data[i] == '^' {
noteId = len(p.notes) + 1
// 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
i++
}
}
@ -479,7 +481,7 @@ func isReference(p *parser, data []byte, tabSize int) int {
hasBlock bool
)
if p.flags&EXTENSION_FOOTNOTES != 0 && noteId > 0 {
if p.flags&EXTENSION_FOOTNOTES != 0 && noteId != 0 {
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
lineEnd = linkEnd
} else {
@ -501,7 +503,6 @@ func isReference(p *parser, data []byte, tabSize int) int {
ref.link = data[idOffset:idEnd]
// if footnote, it's not really a title, it's the contained text
ref.title = raw
p.notes = append(p.notes, ref)
} else {
ref.link = data[linkOffset:linkEnd]
ref.title = data[titleOffset:titleEnd]