Improve footnote handling
- Footnotes separator rather than headline to get around i18n - Warn on footnote redefinition - Do not export footnote definitions at point of definition, only in the footnote section. - Do not automatically exclude Footnotes section to get around possibly hiding other content of such a section - and i18n. The user has the choice of explicitly hiding the section via a :noexport: tag. and some other refactoring
This commit is contained in:
parent
beff0c0d8a
commit
eb7db9b968
11 changed files with 114 additions and 60 deletions
|
@ -1,6 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
for org_file in org/testdata/*.org; do
|
||||
echo $org_file
|
||||
./go-org $org_file html > org/testdata/$(basename $org_file .org).html
|
||||
./go-org $org_file org > org/testdata/$(basename $org_file .org).pretty_org
|
||||
done
|
||||
|
|
|
@ -14,7 +14,7 @@ type Document struct {
|
|||
Path string
|
||||
tokens []token
|
||||
Nodes []Node
|
||||
Footnotes *Footnotes
|
||||
Footnotes Footnotes
|
||||
StatusKeywords []string
|
||||
MaxEmphasisNewLines int
|
||||
AutoLink bool
|
||||
|
@ -72,7 +72,7 @@ func FrontMatterHandler(fm FrontMatter, k, v string) error {
|
|||
|
||||
func NewDocument() *Document {
|
||||
return &Document{
|
||||
Footnotes: &Footnotes{
|
||||
Footnotes: Footnotes{
|
||||
Title: "Footnotes",
|
||||
Definitions: map[string]*FootnoteDefinition{},
|
||||
},
|
||||
|
@ -222,6 +222,16 @@ func (d *Document) parseMany(i int, stop stopFn) (int, []Node) {
|
|||
return i - start, nodes
|
||||
}
|
||||
|
||||
func (d *Document) addFootnote(name string, definition *FootnoteDefinition) {
|
||||
if definition != nil {
|
||||
if _, exists := d.Footnotes.Definitions[name]; exists {
|
||||
d.Log.Printf("Footnote [fn:%s] redefined! %#v", name, definition)
|
||||
}
|
||||
d.Footnotes.Definitions[name] = definition
|
||||
}
|
||||
d.Footnotes.addOrder = append(d.Footnotes.addOrder, name)
|
||||
}
|
||||
|
||||
func tokenize(line string) token {
|
||||
for _, lexFn := range lexFns {
|
||||
if token, ok := lexFn(line); ok {
|
||||
|
|
|
@ -35,17 +35,10 @@ func (d *Document) parseFootnoteDefinition(i int, parentStop stopFn) (int, Node)
|
|||
}
|
||||
consumed, nodes := d.parseMany(i, stop)
|
||||
definition := FootnoteDefinition{name, nodes, false}
|
||||
d.Footnotes.add(name, &definition)
|
||||
d.addFootnote(name, &definition)
|
||||
return consumed, definition
|
||||
}
|
||||
|
||||
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{}
|
||||
|
|
29
org/html.go
29
org/html.go
|
@ -13,11 +13,10 @@ import (
|
|||
|
||||
type HTMLWriter struct {
|
||||
stringBuilder
|
||||
HighlightCodeBlock func(source, lang string) string
|
||||
FootnotesHeadingTitle string
|
||||
htmlEscape bool
|
||||
excludeTags []string
|
||||
log *log.Logger
|
||||
HighlightCodeBlock func(source, lang string) string
|
||||
htmlEscape bool
|
||||
excludeTags []string
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
var emphasisTags = map[string][]string{
|
||||
|
@ -45,8 +44,7 @@ var listItemStatuses = map[string]string{
|
|||
|
||||
func NewHTMLWriter() *HTMLWriter {
|
||||
return &HTMLWriter{
|
||||
htmlEscape: true,
|
||||
FootnotesHeadingTitle: "Footnotes",
|
||||
htmlEscape: true,
|
||||
HighlightCodeBlock: func(source, lang string) string {
|
||||
return fmt.Sprintf("%s\n<pre>\n%s\n</pre>\n</div>", `<div class="highlight">`, html.EscapeString(source))
|
||||
},
|
||||
|
@ -95,7 +93,7 @@ func (w *HTMLWriter) writeNodes(ns ...Node) {
|
|||
continue
|
||||
|
||||
case FootnoteDefinition:
|
||||
w.writeFootnoteDefinition(n)
|
||||
continue
|
||||
|
||||
case List:
|
||||
w.writeList(n)
|
||||
|
@ -191,24 +189,19 @@ func (w *HTMLWriter) writeFootnoteDefinition(f FootnoteDefinition) {
|
|||
}
|
||||
|
||||
func (w *HTMLWriter) writeFootnotes(d *Document) {
|
||||
fs := d.Footnotes
|
||||
if len(fs.Definitions) == 0 {
|
||||
if len(d.Footnotes.Definitions) == 0 {
|
||||
return
|
||||
}
|
||||
w.WriteString(`<div class="footnotes">` + "\n")
|
||||
w.WriteString(`<h1 class="footnotes-title">` + fs.Title + `</h1>` + "\n")
|
||||
w.WriteString(`<hr class="footnotes-separatator">` + "\n")
|
||||
w.WriteString(`<div class="footnote-definitions">` + "\n")
|
||||
for _, definition := range d.Footnotes.Ordered() {
|
||||
w.writeNodes(definition)
|
||||
w.writeFootnoteDefinition(definition)
|
||||
}
|
||||
w.WriteString("</div>\n</div>\n")
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeHeadline(h Headline) {
|
||||
title := w.nodesAsString(h.Title...)
|
||||
if h.Lvl == 1 && title == w.FootnotesHeadingTitle {
|
||||
return
|
||||
}
|
||||
for _, excludeTag := range w.excludeTags {
|
||||
for _, tag := range h.Tags {
|
||||
if excludeTag == tag {
|
||||
|
@ -230,7 +223,7 @@ func (w *HTMLWriter) writeHeadline(h Headline) {
|
|||
w.WriteString(fmt.Sprintf(`<span class="priority">[%s]</span>`, h.Priority) + "\n")
|
||||
}
|
||||
|
||||
w.WriteString(title)
|
||||
w.writeNodes(h.Title...)
|
||||
if len(h.Tags) != 0 {
|
||||
tags := make([]string, len(h.Tags))
|
||||
for i, tag := range h.Tags {
|
||||
|
@ -337,7 +330,7 @@ func (w *HTMLWriter) writeDescriptiveListItem(di DescriptiveListItem) {
|
|||
}
|
||||
|
||||
func (w *HTMLWriter) writeParagraph(p Paragraph) {
|
||||
if isEmptyLineParagraph(p) {
|
||||
if len(p.Children) == 0 {
|
||||
return
|
||||
}
|
||||
w.WriteString("<p>")
|
||||
|
|
|
@ -157,7 +157,7 @@ func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
|
|||
link := FootnoteLink{name, nil}
|
||||
if definition != "" {
|
||||
link.Definition = &FootnoteDefinition{name, []Node{Paragraph{d.parseInline(definition)}}, true}
|
||||
d.Footnotes.add(name, link.Definition)
|
||||
d.addFootnote(name, link.Definition)
|
||||
}
|
||||
return len(m[0]), link
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
|
@ -175,10 +176,11 @@ func (w *OrgWriter) writePropertyDrawer(d PropertyDrawer) {
|
|||
|
||||
func (w *OrgWriter) writeFootnoteDefinition(f FootnoteDefinition) {
|
||||
w.WriteString(fmt.Sprintf("[fn:%s]", f.Name))
|
||||
if !(len(f.Children) >= 1 && isEmptyLineParagraph(f.Children[0])) {
|
||||
content := w.nodesAsString(f.Children...)
|
||||
if content != "" && !unicode.IsSpace(rune(content[0])) {
|
||||
w.WriteString(" ")
|
||||
}
|
||||
w.writeNodes(f.Children...)
|
||||
w.WriteString(content)
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeParagraph(p Paragraph) {
|
||||
|
|
59
org/testdata/footnotes.html
vendored
59
org/testdata/footnotes.html
vendored
|
@ -17,11 +17,43 @@ further references to the same footnote should not <sup class="footnote-referenc
|
|||
inline footnotes are also supported via <sup class="footnote-reference"><a id="footnote-reference-2" href="#footnote-2">2</a></sup>.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Footnote definitions are not printed where they appear.
|
||||
Rather, they are gathered and exported at the end of the document in the footnote section. <sup class="footnote-reference"><a id="footnote-reference-4" href="#footnote-4">4</a></sup>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h1>
|
||||
Footnotes
|
||||
</h1>
|
||||
<p>
|
||||
Please note that the footnotes section is not automatically excluded from the export like in emacs. <sup class="footnote-reference"><a id="footnote-reference-7" href="#footnote-7">7</a></sup>
|
||||
</p>
|
||||
<p>
|
||||
this is not part of <sup class="footnote-reference"><a id="footnote-reference-7" href="#footnote-7">7</a></sup> anymore as there are 2 blank lines in between!
|
||||
</p>
|
||||
<div class="footnotes">
|
||||
<h1 class="footnotes-title">Footnotes</h1>
|
||||
<hr class="footnotes-separatator">
|
||||
<div class="footnote-definitions">
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-4"><a href="#footnote-reference-4">4</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
so this definition will not be at the end of this section in the exported document.
|
||||
Rather, it will be somewhere down below in the footnotes section.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-5"><a href="#footnote-reference-5">5</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
another unused footnote (this definition overwrites the previous definition of <code class="verbatim">fn:5</code>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-1"><a href="#footnote-reference-1">1</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
|
@ -83,22 +115,6 @@ and tables
|
|||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-4"><a href="#footnote-reference-4">4</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
another unused footnote
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-5"><a href="#footnote-reference-5">5</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
another unused footnote
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-6"><a href="#footnote-reference-6">6</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
|
@ -108,6 +124,15 @@ This shouldn't happen when the definition line and the line after that are e
|
|||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-7"><a href="#footnote-reference-7">7</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
There's multiple reasons for that. Among others, doing so requires i18n (to recognize the section) and silently
|
||||
hides content before and after the footnotes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-2"><a href="#footnote-reference-2">2</a></sup>
|
||||
<div class="footnote-body">
|
||||
<p>
|
||||
|
|
22
org/testdata/footnotes.org
vendored
22
org/testdata/footnotes.org
vendored
|
@ -2,8 +2,17 @@
|
|||
- normal footnote reference [fn:1] [fn:6]
|
||||
- further references to the same footnote should not [fn:1] render duplicates in the footnote list
|
||||
- inline footnotes are also supported via [fn:2:the inline footnote definition].
|
||||
- Footnote definitions are not printed where they appear.
|
||||
Rather, they are gathered and exported at the end of the document in the footnote section. [fn:4]
|
||||
|
||||
[fn:4] so this definition will not be at the end of this section in the exported document.
|
||||
Rather, it will be somewhere down below in the footnotes section.
|
||||
|
||||
[fn:5] this definition will also not be exported here - not only that, it will be overwritten by a definition
|
||||
of the same name later on in the document. That will log a warning but carry on nonetheless.
|
||||
* Footnotes
|
||||
Please note that the footnotes section is not automatically excluded from the export like in emacs. [fn:7]
|
||||
|
||||
[fn:1] https://www.example.com
|
||||
- footnotes can contain *markup*
|
||||
- and other elements
|
||||
|
@ -18,11 +27,18 @@
|
|||
|
||||
[fn:3] [[http://example.com/unused-footnote][example.com/unused-footnote]]
|
||||
|
||||
[fn:4] another unused footnote
|
||||
|
||||
[fn:5] another unused footnote
|
||||
[fn:5] another unused footnote (this definition overwrites the previous definition of =fn:5=)
|
||||
|
||||
[fn:6]
|
||||
|
||||
Footnotes break after two consecutive empty lines - just like paragraphs - see https://orgmode.org/worg/dev/org-syntax.html.
|
||||
This shouldn't happen when the definition line and the line after that are empty.
|
||||
|
||||
|
||||
[fn:7]
|
||||
There's multiple reasons for that. Among others, doing so requires i18n (to recognize the section) and silently
|
||||
hides content before and after the footnotes.
|
||||
|
||||
|
||||
|
||||
this is not part of [fn:7] anymore as there are 2 blank lines in between!
|
||||
|
|
22
org/testdata/footnotes.pretty_org
vendored
22
org/testdata/footnotes.pretty_org
vendored
|
@ -2,8 +2,17 @@
|
|||
- normal footnote reference [fn:1] [fn:6]
|
||||
- further references to the same footnote should not [fn:1] render duplicates in the footnote list
|
||||
- inline footnotes are also supported via [fn:2:the inline footnote definition].
|
||||
- Footnote definitions are not printed where they appear.
|
||||
Rather, they are gathered and exported at the end of the document in the footnote section. [fn:4]
|
||||
|
||||
[fn:4] so this definition will not be at the end of this section in the exported document.
|
||||
Rather, it will be somewhere down below in the footnotes section.
|
||||
|
||||
[fn:5] this definition will also not be exported here - not only that, it will be overwritten by a definition
|
||||
of the same name later on in the document. That will log a warning but carry on nonetheless.
|
||||
* Footnotes
|
||||
Please note that the footnotes section is not automatically excluded from the export like in emacs. [fn:7]
|
||||
|
||||
[fn:1] https://www.example.com
|
||||
- footnotes can contain *markup*
|
||||
- and other elements
|
||||
|
@ -18,11 +27,18 @@
|
|||
|
||||
[fn:3] [[http://example.com/unused-footnote][example.com/unused-footnote]]
|
||||
|
||||
[fn:4] another unused footnote
|
||||
|
||||
[fn:5] another unused footnote
|
||||
[fn:5] another unused footnote (this definition overwrites the previous definition of =fn:5=)
|
||||
|
||||
[fn:6]
|
||||
|
||||
Footnotes break after two consecutive empty lines - just like paragraphs - see https://orgmode.org/worg/dev/org-syntax.html.
|
||||
This shouldn't happen when the definition line and the line after that are empty.
|
||||
|
||||
|
||||
[fn:7]
|
||||
There's multiple reasons for that. Among others, doing so requires i18n (to recognize the section) and silently
|
||||
hides content before and after the footnotes.
|
||||
|
||||
|
||||
|
||||
this is not part of [fn:7] anymore as there are 2 blank lines in between!
|
||||
|
|
5
org/testdata/misc.html
vendored
5
org/testdata/misc.html
vendored
|
@ -359,8 +359,11 @@ When inserting an image link like <img src="/home/amos/Pictures/Screenshots/img-
|
|||
(ansi-term))
|
||||
</pre>
|
||||
</div>
|
||||
<h1>
|
||||
Footnotes
|
||||
</h1>
|
||||
<div class="footnotes">
|
||||
<h1 class="footnotes-title">Footnotes</h1>
|
||||
<hr class="footnotes-separatator">
|
||||
<div class="footnote-definitions">
|
||||
<div class="footnote-definition">
|
||||
<sup id="footnote-1"><a href="#footnote-reference-1">1</a></sup>
|
||||
|
|
|
@ -11,11 +11,6 @@ func isSecondBlankLine(d *Document, i int) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func isEmptyLineParagraph(n Node) bool {
|
||||
p, ok := n.(Paragraph)
|
||||
return ok && len(p.Children) == 0
|
||||
}
|
||||
|
||||
func isImageOrVideoLink(n Node) bool {
|
||||
if l, ok := n.(RegularLink); ok && l.Kind() == "video" || l.Kind() == "image" {
|
||||
return true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue