From b72024d2b0824a752171261563664f6cbfa2706b Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 15 Jan 2020 18:47:28 -0700 Subject: [PATCH] Use user input as part of footnote identifier Named footnotes (`[^foo]` instead of `[^42]`) use the name in HTML node identifiers. This allows for stable references. Cool URLs don't change. Fixes #92 --- extension/_test/footnote.txt | 18 ++++++++++++++++++ extension/footnote.go | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/extension/_test/footnote.txt b/extension/_test/footnote.txt index 9b06721..6c82688 100644 --- a/extension/_test/footnote.txt +++ b/extension/_test/footnote.txt @@ -66,3 +66,21 @@ test![^1] //= = = = = = = = = = = = = = = = = = = = = = = =// + + +6 +//- - - - - - - - -// +That's some text with a footnote.[^mynameisjeff] + +[^mynameisjeff]: And that's the footnote. +//- - - - - - - - -// +

That's some text with a footnote.1

+
+
+
    +
  1. +

    And that's the footnote. ↩︎

    +
  2. +
+
+//= = = = = = = = = = = = = = = = = = = = = = = =// diff --git a/extension/footnote.go b/extension/footnote.go index 972418e..bc0ec74 100644 --- a/extension/footnote.go +++ b/extension/footnote.go @@ -242,16 +242,36 @@ func (r *FootnoteHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegist reg.Register(ast.KindFootnoteList, r.renderFootnoteList) } +func isNumeric(b []byte) bool { + for _, c := range b { + if c < '0' || c > '9' { + return false + } + } + return true +} + +// footnoteRef decides what to use to refer to the footnote. The +// returned string is safe to use in HTML attributes. +func footnoteRef(index int, ref []byte) []byte { + if !isNumeric(ref) { + return util.EscapeHTML(ref) + } + return strconv.AppendInt(nil, int64(index), 10) +} + func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) { if entering { n := node.(*ast.FootnoteLink) - is := strconv.Itoa(n.Index) + ref := footnoteRef(n.Index, n.Ref) _, _ = w.WriteString(``) - _, _ = w.WriteString(is) + // the content itself refers to footnotes by number, even when + // they are named + _, _ = w.WriteString(strconv.Itoa(n.Index)) _, _ = w.WriteString(``) } return gast.WalkContinue, nil @@ -260,9 +280,9 @@ func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byt func (r *FootnoteHTMLRenderer) renderFootnoteBackLink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) { if entering { n := node.(*ast.FootnoteBackLink) - is := strconv.Itoa(n.Index) + ref := footnoteRef(n.Index, n.Ref) _, _ = w.WriteString(` `) _, _ = w.WriteString("↩︎") _, _ = w.WriteString(``) @@ -272,10 +292,10 @@ func (r *FootnoteHTMLRenderer) renderFootnoteBackLink(w util.BufWriter, source [ func (r *FootnoteHTMLRenderer) renderFootnote(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) { n := node.(*ast.Footnote) - is := strconv.Itoa(n.Index) if entering { + ref := footnoteRef(n.Index, n.Ref) _, _ = w.WriteString(`