Add support for Table of Contents

This commit is contained in:
Niklas Fasching 2018-12-26 16:00:27 +01:00
parent eb7db9b968
commit d921a68a55
7 changed files with 218 additions and 41 deletions

View file

@ -15,6 +15,7 @@ type Document struct {
tokens []token
Nodes []Node
Footnotes Footnotes
Outline Outline
StatusKeywords []string
MaxEmphasisNewLines int
AutoLink bool
@ -71,6 +72,7 @@ func FrontMatterHandler(fm FrontMatter, k, v string) error {
}
func NewDocument() *Document {
outlineSection := &Section{}
return &Document{
Footnotes: Footnotes{
Title: "Footnotes",
@ -78,6 +80,7 @@ func NewDocument() *Document {
},
AutoLink: true,
MaxEmphasisNewLines: 1,
Outline: Outline{outlineSection, outlineSection, 0},
BufferSettings: map[string]string{},
DefaultSettings: map[string]string{
"TODO": "TODO | DONE",
@ -232,6 +235,12 @@ func (d *Document) addFootnote(name string, definition *FootnoteDefinition) {
d.Footnotes.addOrder = append(d.Footnotes.addOrder, name)
}
func (d *Document) addHeadline(headline *Headline) int {
d.Outline.last.add(&Section{Headline: headline})
d.Outline.count++
return d.Outline.count
}
func tokenize(line string) token {
for _, lexFn := range lexFns {
if token, ok := lexFn(line); ok {

View file

@ -1,12 +1,26 @@
package org
import (
"fmt"
"regexp"
"strings"
"unicode"
)
type Outline struct {
*Section
last *Section
count int
}
type Section struct {
Headline *Headline
Parent *Section
Children []*Section
}
type Headline struct {
Index int
Lvl int
Status string
Priority string
@ -29,6 +43,9 @@ func lexHeadline(line string) (token, bool) {
func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) {
t, headline := d.tokens[i], Headline{}
headline.Lvl = len(t.matches[1])
headline.Index = d.addHeadline(&headline)
text := t.content
todoKeywords := strings.FieldsFunc(d.Get("TODO"), func(r rune) bool { return unicode.IsSpace(r) || r == '|' })
for _, k := range todoKeywords {
@ -64,3 +81,19 @@ func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) {
headline.Children = nodes
return consumed + 1, headline
}
func (h Headline) ID() string {
if customID, ok := h.Properties.Get("CUSTOM_ID"); ok {
return customID
}
return fmt.Sprintf("headline-%d", h.Index)
}
func (parent *Section) add(current *Section) {
if parent.Headline == nil || parent.Headline.Lvl < current.Headline.Lvl {
parent.Children = append(parent.Children, current)
current.Parent = parent
} else {
parent.Parent.add(current)
}
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"html"
"log"
"regexp"
"strings"
"unicode"
@ -42,6 +43,8 @@ var listItemStatuses = map[string]string{
"X": "checked",
}
var cleanHeadlineTitleForHTMLAnchorRegexp = regexp.MustCompile(`</?a[^>]*>`) // nested a tags are not valid HTML
func NewHTMLWriter() *HTMLWriter {
return &HTMLWriter{
htmlEscape: true,
@ -66,6 +69,7 @@ func (w *HTMLWriter) nodesAsString(nodes ...Node) string {
func (w *HTMLWriter) before(d *Document) {
w.excludeTags = strings.Fields(d.Get("EXCLUDE_TAGS"))
w.log = d.Log
w.writeOutline(d)
}
func (w *HTMLWriter) after(d *Document) {
@ -201,6 +205,31 @@ func (w *HTMLWriter) writeFootnotes(d *Document) {
w.WriteString("</div>\n</div>\n")
}
func (w *HTMLWriter) writeOutline(d *Document) {
if len(d.Outline.Children) != 0 {
w.WriteString("<nav>\n<ul>\n")
for _, section := range d.Outline.Children {
w.writeSection(section)
}
w.WriteString("</ul>\n</nav>\n")
}
}
func (w *HTMLWriter) writeSection(section *Section) {
w.WriteString("<li>\n")
h := section.Headline
title := cleanHeadlineTitleForHTMLAnchorRegexp.ReplaceAllString(w.nodesAsString(h.Title...), "")
w.WriteString(fmt.Sprintf("<a href=\"#%s\">%s</a>\n", h.ID(), title))
if len(section.Children) != 0 {
w.WriteString("<ul>\n")
for _, section := range section.Children {
w.writeSection(section)
}
w.WriteString("</ul>\n")
}
w.WriteString("</li>\n")
}
func (w *HTMLWriter) writeHeadline(h Headline) {
for _, excludeTag := range w.excludeTags {
for _, tag := range h.Tags {
@ -210,12 +239,7 @@ func (w *HTMLWriter) writeHeadline(h Headline) {
}
}
if id, ok := h.Properties.Get("CUSTOM_ID"); ok {
w.WriteString(fmt.Sprintf(`<h%d id="%s">`, h.Lvl, id) + "\n")
} else {
w.WriteString(fmt.Sprintf("<h%d>\n", h.Lvl))
}
w.WriteString(fmt.Sprintf(`<h%d id="%s">`, h.Lvl, h.ID()) + "\n")
if h.Status != "" {
w.WriteString(fmt.Sprintf(`<span class="todo">%s</span>`, h.Status) + "\n")
}

View file

@ -1,4 +1,14 @@
<h1>
<nav>
<ul>
<li>
<a href="#headline-1">Using some footnotes</a>
</li>
<li>
<a href="#headline-2">Footnotes</a>
</li>
</ul>
</nav>
<h1 id="headline-1">
Using some footnotes
</h1>
<ul>
@ -24,7 +34,7 @@ Rather, they are gathered and exported at the end of the document in the footnot
</p>
</li>
</ul>
<h1>
<h1 id="headline-2">
Footnotes
</h1>
<p>

View file

@ -1,4 +1,26 @@
<h1>
<nav>
<ul>
<li>
<a href="#headline-1">Simple Headline <code class="statistic">[1/2]</code></a>
</li>
<li>
<a href="#headline-2">Headline with todo status &amp; priority</a>
</li>
<li>
<a href="#this-will-be-the-id-of-the-headline">Headline with TODO status</a>
</li>
<li>
<a href="#headline-4">Headline with tags &amp; priority</a>
</li>
<li>
<a href="#headline-5">headline with custom status</a>
</li>
<li>
<a href="#headline-6">excluded headline</a>
</li>
</ul>
</nav>
<h1 id="headline-1">
Simple Headline <code class="statistic">[1/2]</code>
</h1>
<ul>
@ -20,7 +42,7 @@ not just where they are actually meant to be - even here &gt; <code class="stati
</p>
</li>
</ul>
<h1>
<h1 id="headline-2">
<span class="todo">TODO</span>
<span class="priority">[B]</span>
Headline with todo status &amp; priority
@ -32,7 +54,7 @@ Headline with TODO status
<p>
we can link to headlines that define a custom_id: <a href="#this-will-be-the-id-of-the-headline">#this-will-be-the-id-of-the-headline</a>
</p>
<h1>
<h1 id="headline-4">
<span class="priority">[A]</span>
Headline with tags &amp; priority&#xa0;&#xa0;&#xa0;<span class="tags"><span>foo</span>&#xa0;<span>bar</span></span>
</h1>
@ -45,7 +67,7 @@ This is inside the drawer
<p>
Still outside the drawer
</p>
<h1>
<h1 id="headline-5">
<span class="todo">CUSTOM</span>
headline with custom status
</h1>

124
org/testdata/misc.html vendored
View file

@ -1,12 +1,88 @@
<h2>
<nav>
<ul>
<li>
<a href="#headline-1">issues from goorgeous (free test cases, yay!)</a>
</li>
<li>
<a href="#headline-2">#19: Support #+HTML</a>
</li>
<li>
<a href="#headline-3">#29: Support verse block</a>
</li>
<li>
<a href="#headline-4">#31: Support #+INCLUDE</a>
</li>
<li>
<a href="#headline-5">#33: Wrong output when mixing html with Org mode</a>
</li>
<li>
<a href="#headline-6">#46: Support for symbols like ndash and mdash</a>
</li>
<li>
<a href="#headline-7">#47: Consecutive <code>code</code> wrapped text gets joined</a>
</li>
<li>
<a href="#headline-8">#50: LineBreaks in lists are preserved</a>
</li>
<li>
<a href="#headline-9">#68: Quote block with inline markup</a>
</li>
<li>
<a href="#headline-10">#72: Support for #+ATTR_HTML</a>
</li>
<li>
<a href="#headline-11">#75: Not parsing nested lists correctly</a>
</li>
<li>
<a href="#headline-12">#77: Recognize <code class="verbatim">code</code>— as code plus dash</a>
</li>
<li>
<a href="#headline-13">#78: Emphasis at beginning of line</a>
</li>
<li>
<a href="#headline-14">#82: Crash on empty headline</a>
</li>
<li>
<a href="#headline-15"></a>
</li>
<li>
<a href="#headline-16">#84: Paragraphs that are not followed by an empty line are not parsed correctly</a>
</li>
<li>
<a href="#headline-17">Foo</a>
</li>
<li>
<a href="#headline-18">Bar</a>
</li>
<li>
<a href="#headline-19">#86: Multiple hyphens not converted to dashes</a>
</li>
<li>
<a href="#headline-20">#87: Markup in footnotes is rendered literally</a>
</li>
<li>
<a href="#headline-21">issues (wrongly) filed with hugo</a>
</li>
<li>
<a href="#headline-22">#3874 exporting images in org mode</a>
</li>
<li>
<a href="#headline-23">#4006 source code blocks in org not rendered correctly</a>
</li>
<li>
<a href="#headline-24">Footnotes</a>
</li>
</ul>
</nav>
<h2 id="headline-1">
issues from goorgeous (free test cases, yay!)
</h2>
<h3>
<h3 id="headline-2">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/19">#19</a>: Support #+HTML
</h3>
<p style="border: 1px dotted grey">neato!</p>
<h3>
<h3 id="headline-3">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/29">#29:</a> Support verse block
</h3>
@ -23,7 +99,7 @@ or even a <strong>totally</strong> <em>custom</em> kind of block
crazy ain&#39;t it?
</p>
</div>
<h3>
<h3 id="headline-4">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/31">#31</a>: Support #+INCLUDE
</h3>
@ -115,7 +191,7 @@ deploy:
</pre>
</li>
</ul>
<h3>
<h3 id="headline-5">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/33">#33</a>: Wrong output when mixing html with Org mode
</h3>
@ -133,7 +209,7 @@ deploy:
</tbody>
</table>
</div>
<h3>
<h3 id="headline-6">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/46">#46</a>: Support for symbols like ndash and mdash
</h3>
@ -164,7 +240,7 @@ note that —— is replaced with 2 mdashes and …. becomes ellipsis+. and so o
</p>
</li>
</ul>
<h3>
<h3 id="headline-7">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/47">#47:</a> Consecutive <code>code</code> wrapped text gets joined
</h3>
@ -173,7 +249,7 @@ either <code>this</code> or <code>that</code> foo.
either <code>this</code>
or <code>that</code> foo.
</p>
<h3>
<h3 id="headline-8">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/50">#50</a>: LineBreaks in lists are preserved
</h3>
@ -208,7 +284,7 @@ foo
</p>
</li>
</ol>
<h3>
<h3 id="headline-9">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/68">#68</a>: Quote block with inline markup
</h3>
@ -217,12 +293,12 @@ foo
<a href="https://www.example.com"><em>this</em> <strong>is</strong> <span style="text-decoration: underline;">markup</span>!</a>
</p>
</blockquote>
<h3>
<h3 id="headline-10">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/72">#72</a>: Support for #+ATTR_HTML
</h3>
<img src="https://golang.org/doc/gopher/pkg.png" alt="Go is fine though." title="https://golang.org/doc/gopher/pkg.png" id="gopher-image" width="300" style="border:2px solid black;"/>
<h3>
<h3 id="headline-11">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/75">#75</a>: Not parsing nested lists correctly
</h3>
@ -240,11 +316,11 @@ sub bullet
</ul>
</li>
</ul>
<h3>
<h3 id="headline-12">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/77">#77</a>: Recognize <code class="verbatim">code</code>— as code plus dash
</h3>
<h3>
<h3 id="headline-13">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/78">#78</a>: Emphasis at beginning of line
</h3>
@ -255,33 +331,33 @@ sub bullet
Text
<em>italics</em>
</p>
<h3>
<h3 id="headline-14">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/82">#82</a>: Crash on empty headline
</h3>
<h4>
<h4 id="headline-15">
</h4>
<p>
just a space as title…
</p>
<h3>
<h3 id="headline-16">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/84">#84</a>: Paragraphs that are not followed by an empty line are not parsed correctly
</h3>
<h4>
<h4 id="headline-17">
Foo
</h4>
<p>
Foo paragraph.
</p>
<h4>
<h4 id="headline-18">
Bar
</h4>
<p>
Bar paragraph
</p>
<h3>
<h3 id="headline-19">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/86">#86</a>: Multiple hyphens not converted to dashes
</h3>
@ -327,17 +403,17 @@ src/example/export blocks should not be converted!
</pre>
</li>
</ul>
<h3>
<h3 id="headline-20">
<span class="todo">DONE</span>
<a href="https://github.com/chaseadamsio/goorgeous/issues/87">#87</a>: Markup in footnotes is rendered literally
</h3>
<p>
footnotes can contain <strong>markup</strong> - and other elements and stuff <sup class="footnote-reference"><a id="footnote-reference-2" href="#footnote-2">2</a></sup>
</p>
<h2>
<h2 id="headline-21">
issues (wrongly) filed with hugo
</h2>
<h3>
<h3 id="headline-22">
<a href="https://github.com/gohugoio/hugo/issues/3874">#3874</a> exporting images in org mode
</h3>
<p>
@ -346,7 +422,7 @@ Hello, I&#39;m writing hugo blogs using org-mode.
<p>
When inserting an image link like <img src="/home/amos/Pictures/Screenshots/img-2017-09-11-165647.png" alt="/home/amos/Pictures/Screenshots/img-2017-09-11-165647.png" title="/home/amos/Pictures/Screenshots/img-2017-09-11-165647.png" />, hugo doesn&#39;t export the image.
</p>
<h3>
<h3 id="headline-23">
<a href="https://github.com/gohugoio/hugo/issues/4006">#4006</a> source code blocks in org not rendered correctly
</h3>
<div class="highlight">
@ -359,7 +435,7 @@ When inserting an image link like <img src="/home/amos/Pictures/Screenshots/img-
(ansi-term))
</pre>
</div>
<h1>
<h1 id="headline-24">
Footnotes
</h1>
<div class="footnotes">