diff --git a/org/document.go b/org/document.go index f7d27d7..d061712 100644 --- a/org/document.go +++ b/org/document.go @@ -10,7 +10,7 @@ import ( type Document struct { tokens []token Nodes []Node - Footnotes Footnotes + Footnotes *Footnotes StatusKeywords []string MaxEmphasisNewLines int BufferSettings map[string]string @@ -52,10 +52,10 @@ var nilToken = token{"nil", -1, "", nil} func NewDocument() *Document { return &Document{ - Footnotes: Footnotes{ + Footnotes: &Footnotes{ ExcludeHeading: true, Title: "Footnotes", - Definitions: map[string]FootnoteDefinition{}, + Definitions: map[string]*FootnoteDefinition{}, }, MaxEmphasisNewLines: 1, BufferSettings: map[string]string{}, @@ -132,7 +132,7 @@ func (d *Document) parseOne(i int, stop stopFn) (consumed int, node Node) { func (d *Document) parseMany(i int, stop stopFn) (int, []Node) { start, nodes := i, []Node{} - for i < len(d.tokens) { + for i < len(d.tokens) && !stop(d, i) { consumed, node := d.parseOne(i, stop) i += consumed nodes = append(nodes, node) diff --git a/org/footnote.go b/org/footnote.go index 147ddcd..5bea596 100644 --- a/org/footnote.go +++ b/org/footnote.go @@ -7,8 +7,8 @@ import ( type Footnotes struct { ExcludeHeading bool Title string - Definitions map[string]FootnoteDefinition - Order []string + Definitions map[string]*FootnoteDefinition + addOrder []string } type FootnoteDefinition struct { @@ -34,6 +34,29 @@ func (d *Document) parseFootnoteDefinition(i int, parentStop stopFn) (int, Node) d.tokens[i].kind == "headline" || d.tokens[i].kind == "footnoteDefinition" } consumed, nodes := d.parseMany(i, stop) - d.Footnotes.Definitions[name] = FootnoteDefinition{name, nodes, false} + d.Footnotes.add(name, &FootnoteDefinition{name, nodes, false}) return consumed, nil } + +func (fs *Footnotes) add(name string, definition *FootnoteDefinition) { + if definition != nil { + fs.Definitions[name] = definition + } + fs.addOrder = append(fs.addOrder, name) +} + +func (fs *Footnotes) Ordered() []FootnoteDefinition { + m := map[string]bool{} + definitions, inlineDefinitions := []FootnoteDefinition{}, []FootnoteDefinition{} + for _, name := range fs.addOrder { + if isDuplicate := m[name]; !isDuplicate { + m[name] = true + if definition := *fs.Definitions[name]; definition.Inline { + inlineDefinitions = append(inlineDefinitions, definition) + } else { + definitions = append(definitions, definition) + } + } + } + return append(definitions, inlineDefinitions...) +} diff --git a/org/html.go b/org/html.go index 277af26..765b597 100644 --- a/org/html.go +++ b/org/html.go @@ -34,7 +34,7 @@ var listTags = map[string][]string{ func NewHTMLWriter() *HTMLWriter { return &HTMLWriter{ HighlightCodeBlock: func(source, lang string) string { - return fmt.Sprintf("
%s", html.EscapeString(source)) + return fmt.Sprintf("%s", html.EscapeString(source)) }, } } @@ -154,8 +154,8 @@ func (w *HTMLWriter) writeFootnotes(d *Document) { w.WriteString(`` + "\n") w.WriteString(`\n") } diff --git a/org/inline.go b/org/inline.go index 62bf0d3..f891476 100644 --- a/org/inline.go +++ b/org/inline.go @@ -114,19 +114,11 @@ func (d *Document) parseRegularLinkOrFootnoteReference(input string, start int) func (d *Document) parseFootnoteReference(input string, start int) (int, Node) { if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil { name, definition := m[1], m[3] - seen, link := false, FootnoteLink{name, nil} - for _, otherName := range d.Footnotes.Order { - if name == otherName { - seen = true - } - } - if !seen { - d.Footnotes.Order = append(d.Footnotes.Order, name) - } + link := FootnoteLink{name, nil} if definition != "" { link.Definition = &FootnoteDefinition{name, d.parseInline(definition), true} - d.Footnotes.Definitions[name] = *link.Definition } + d.Footnotes.add(name, link.Definition) return len(m[0]), link } return 0, nil diff --git a/org/org.go b/org/org.go index 203981c..15ae21b 100644 --- a/org/org.go +++ b/org/org.go @@ -145,9 +145,9 @@ func (w *OrgWriter) writeFootnotes(d *Document) { return } w.WriteString("* " + fs.Title + "\n") - for _, name := range fs.Order { - if fnDefinition := fs.Definitions[name]; !fnDefinition.Inline { - w.writeNodes(fnDefinition) + for _, definition := range fs.Ordered() { + if !definition.Inline { + w.writeNodes(definition) } } } diff --git a/org/testdata/example.html b/org/testdata/example.html index 96d9397..d5a95b8 100644 --- a/org/testdata/example.html +++ b/org/testdata/example.html @@ -24,7 +24,7 @@` + fs.Title + `
` + "\n") w.WriteString(`` + "\n") - for _, name := range fs.Order { - w.writeNodes(fs.Definitions[name]) + for _, definition := range d.Footnotes.Ordered() { + w.writeNodes(definition) } w.WriteString("\n
and another subitem
-
echo with a block+echo with a blockand another one with a table
@@ -66,12 +66,12 @@blocks
-
echo a bash source block+echo a bash source block
a source block without a language and a second line -and a third one+and a third onean example blockwith multiple lines @@ -90,7 +90,51 @@ Mongodb is very webscale1 -+ +Foobar
+ ++
+- footnotes can contain markup
+- +
and other elements
++
+- +
like blocks
++
+other non-plain+- +
and tables
++
++ + +1 a ++ + +2 b ++ + +3 c ++4 ++another unused footnote
++5 ++another unused footnote
++6 +another unused footnote
2 diff --git a/org/testdata/example.org b/org/testdata/example.org index ad29d1b..eb93aea 100644 --- a/org/testdata/example.org +++ b/org/testdata/example.org @@ -76,4 +76,22 @@ Mongodb is very webscale - inline footnotes are also supported via [fn:2:the inline footnote definition]. * Footnotes -[fn:1] Foobar +[fn:1] https://www.example.com +- footnotes can contain *markup* +- and other elements + - like blocks + #+BEGIN_SRC + other non-plain + #+END_SRC + - and tables + | 1 | a | + | 2 | b | + | 3 | c | + +[fn:3] [[http://example.com/unused-footnote][example.com/unused-footnote]] + +[fn:4] another unused footnote + +[fn:5] another unused footnote + +[fn:6] another unused footnote