Fix footnote ordering and some other bugs

This commit is contained in:
Niklas Fasching 2018-12-02 20:09:40 +01:00
parent b1f9bfc9e9
commit d5bf4317b2
7 changed files with 105 additions and 28 deletions

View file

@ -10,7 +10,7 @@ import (
type Document struct { type Document struct {
tokens []token tokens []token
Nodes []Node Nodes []Node
Footnotes Footnotes Footnotes *Footnotes
StatusKeywords []string StatusKeywords []string
MaxEmphasisNewLines int MaxEmphasisNewLines int
BufferSettings map[string]string BufferSettings map[string]string
@ -52,10 +52,10 @@ var nilToken = token{"nil", -1, "", nil}
func NewDocument() *Document { func NewDocument() *Document {
return &Document{ return &Document{
Footnotes: Footnotes{ Footnotes: &Footnotes{
ExcludeHeading: true, ExcludeHeading: true,
Title: "Footnotes", Title: "Footnotes",
Definitions: map[string]FootnoteDefinition{}, Definitions: map[string]*FootnoteDefinition{},
}, },
MaxEmphasisNewLines: 1, MaxEmphasisNewLines: 1,
BufferSettings: map[string]string{}, 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) { func (d *Document) parseMany(i int, stop stopFn) (int, []Node) {
start, nodes := i, []Node{} start, nodes := i, []Node{}
for i < len(d.tokens) { for i < len(d.tokens) && !stop(d, i) {
consumed, node := d.parseOne(i, stop) consumed, node := d.parseOne(i, stop)
i += consumed i += consumed
nodes = append(nodes, node) nodes = append(nodes, node)

View file

@ -7,8 +7,8 @@ import (
type Footnotes struct { type Footnotes struct {
ExcludeHeading bool ExcludeHeading bool
Title string Title string
Definitions map[string]FootnoteDefinition Definitions map[string]*FootnoteDefinition
Order []string addOrder []string
} }
type FootnoteDefinition struct { 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" d.tokens[i].kind == "headline" || d.tokens[i].kind == "footnoteDefinition"
} }
consumed, nodes := d.parseMany(i, stop) 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 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...)
}

View file

@ -34,7 +34,7 @@ var listTags = map[string][]string{
func NewHTMLWriter() *HTMLWriter { func NewHTMLWriter() *HTMLWriter {
return &HTMLWriter{ return &HTMLWriter{
HighlightCodeBlock: func(source, lang string) string { HighlightCodeBlock: func(source, lang string) string {
return fmt.Sprintf("<pre>%s<pre>", html.EscapeString(source)) return fmt.Sprintf("<pre>%s</pre>", html.EscapeString(source))
}, },
} }
} }
@ -154,8 +154,8 @@ func (w *HTMLWriter) writeFootnotes(d *Document) {
w.WriteString(`<div id="footnotes">` + "\n") w.WriteString(`<div id="footnotes">` + "\n")
w.WriteString(`<h1 class="footnotes-title">` + fs.Title + `</h1>` + "\n") w.WriteString(`<h1 class="footnotes-title">` + fs.Title + `</h1>` + "\n")
w.WriteString(`<div class="footnote-definitions">` + "\n") w.WriteString(`<div class="footnote-definitions">` + "\n")
for _, name := range fs.Order { for _, definition := range d.Footnotes.Ordered() {
w.writeNodes(fs.Definitions[name]) w.writeNodes(definition)
} }
w.WriteString("</div>\n</div>\n") w.WriteString("</div>\n</div>\n")
} }

View file

@ -114,19 +114,11 @@ func (d *Document) parseRegularLinkOrFootnoteReference(input string, start int)
func (d *Document) parseFootnoteReference(input string, start int) (int, Node) { func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil { if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil {
name, definition := m[1], m[3] name, definition := m[1], m[3]
seen, link := false, FootnoteLink{name, nil} link := FootnoteLink{name, nil}
for _, otherName := range d.Footnotes.Order {
if name == otherName {
seen = true
}
}
if !seen {
d.Footnotes.Order = append(d.Footnotes.Order, name)
}
if definition != "" { if definition != "" {
link.Definition = &FootnoteDefinition{name, d.parseInline(definition), true} 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 len(m[0]), link
} }
return 0, nil return 0, nil

View file

@ -145,9 +145,9 @@ func (w *OrgWriter) writeFootnotes(d *Document) {
return return
} }
w.WriteString("* " + fs.Title + "\n") w.WriteString("* " + fs.Title + "\n")
for _, name := range fs.Order { for _, definition := range fs.Ordered() {
if fnDefinition := fs.Definitions[name]; !fnDefinition.Inline { if !definition.Inline {
w.writeNodes(fnDefinition) w.writeNodes(definition)
} }
} }
} }

View file

@ -24,7 +24,7 @@
<ol> <ol>
<li><p>and another subitem</p> <li><p>and another subitem</p>
<code class="src src-sh"> <code class="src src-sh">
<pre>echo with a block<pre> <pre>echo with a block</pre>
</code> </code>
</li> </li>
<li><p>and another one with a table</p> <li><p>and another one with a table</p>
@ -66,12 +66,12 @@
</ul> </ul>
<h2>blocks</h2> <h2>blocks</h2>
<code class="src src-bash"> <code class="src src-bash">
<pre>echo a bash source block<pre> <pre>echo a bash source block</pre>
</code> </code>
<code class="src src-text"> <code class="src src-text">
<pre>a source block without a language <pre>a source block without a language
and a second line and a second line
and a third one<pre> and a third one</pre>
</code> </code>
<pre class="example"> <pre class="example">
an example blockwith multiple lines an example blockwith multiple lines
@ -90,7 +90,51 @@ Mongodb is very webscale
<div class="footnote-definitions"> <div class="footnote-definitions">
<div class="footnote-definition"> <div class="footnote-definition">
<sup id="footnote-1">1</sup> <sup id="footnote-1">1</sup>
<p>Foobar</p> <p><a href="https://www.example.com">https://www.example.com</a></p>
<ul>
<li>footnotes can contain <strong>markup</strong></li>
<li><p>and other elements</p>
<ul>
<li><p>like blocks</p>
<code class="src src-text">
<pre>other non-plain</pre>
</code>
</li>
<li><p>and tables</p>
<table><tbody>
<tr>
<td>1</td><td>a</td>
</tr>
<tr>
<td>2</td><td>b</td>
</tr>
<tr>
<td>3</td><td>c</td>
</tr>
</tbody>
</table>
</li>
</ul>
</li>
</ul>
</div>
<div class="footnote-definition">
<sup id="footnote-3">3</sup>
<p><a href="http://example.com/unused-footnote">example.com/unused-footnote</a> </p>
</div>
<div class="footnote-definition">
<sup id="footnote-4">4</sup>
<p>another unused footnote </p>
</div>
<div class="footnote-definition">
<sup id="footnote-5">5</sup>
<p>another unused footnote </p>
</div>
<div class="footnote-definition">
<sup id="footnote-6">6</sup>
<p>another unused footnote</p>
</div> </div>
<div class="footnote-definition"> <div class="footnote-definition">
<sup id="footnote-2">2</sup> <sup id="footnote-2">2</sup>

View file

@ -76,4 +76,22 @@ Mongodb is very webscale
- inline footnotes are also supported via [fn:2:the inline footnote definition]. - inline footnotes are also supported via [fn:2:the inline footnote definition].
* Footnotes * 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