Add some documentation & split Document into Configuration+Document
This commit is contained in:
parent
c98cdea4f0
commit
56ecb2d401
9 changed files with 77 additions and 44 deletions
|
@ -17,7 +17,7 @@ func main() {
|
||||||
|
|
||||||
js.Global().Set("run", js.NewCallback(func([]js.Value) {
|
js.Global().Set("run", js.NewCallback(func([]js.Value) {
|
||||||
in := strings.NewReader(in.Get("value").String())
|
in := strings.NewReader(in.Get("value").String())
|
||||||
html, err := org.NewDocument().Parse(in).Write(org.NewHTMLWriter())
|
html, err := org.New().Parse(in, "").Write(org.NewHTMLWriter())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.Set("innerHTML", fmt.Sprintf("<pre>%s</pre>", err))
|
out.Set("innerHTML", fmt.Sprintf("<pre>%s</pre>", err))
|
||||||
} else {
|
} else {
|
||||||
|
|
2
main.go
2
main.go
|
@ -27,7 +27,7 @@ func main() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
out, err := "", nil
|
out, err := "", nil
|
||||||
d := org.NewDocument().SetPath(path).Parse(bytes.NewReader(bs))
|
d := org.New().Parse(bytes.NewReader(bs), path)
|
||||||
switch strings.ToLower(os.Args[2]) {
|
switch strings.ToLower(os.Args[2]) {
|
||||||
case "org":
|
case "org":
|
||||||
out, err = d.Write(org.NewOrgWriter())
|
out, err = d.Write(org.NewOrgWriter())
|
||||||
|
|
102
org/document.go
102
org/document.go
|
@ -1,3 +1,15 @@
|
||||||
|
// Package org is an Org mode syntax processor.
|
||||||
|
//
|
||||||
|
// It parses plain text into an AST and can export it as HTML or pretty printed Org mode syntax.
|
||||||
|
// Further export formats can be defined using the Writer interface.
|
||||||
|
//
|
||||||
|
// You probably want to start with something like this:
|
||||||
|
// input := strings.NewReader("Your Org mode input")
|
||||||
|
// html, err := org.New().Parse(input, "./").Write(org.NewHTMLWriter())
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("Something went wrong: %s", err)
|
||||||
|
// }
|
||||||
|
// log.Print(html)
|
||||||
package org
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -10,27 +22,34 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Configuration struct {
|
||||||
|
MaxEmphasisNewLines int // Maximum number of newlines inside an emphasis. See org-emphasis-regexp-components newline.
|
||||||
|
AutoLink bool // Try to convert text passages that look like hyperlinks into hyperlinks.
|
||||||
|
DefaultSettings map[string]string // Default values for settings that are overriden by setting the same key in BufferSettings.
|
||||||
|
Log *log.Logger // Log is used to print warnings during parsing.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document contains the parsing results and a pointer to the Configuration.
|
||||||
type Document struct {
|
type Document struct {
|
||||||
Path string
|
*Configuration
|
||||||
tokens []token
|
Path string // Path of the file containing the parse input - used to resolve relative paths during parsing (e.g. INCLUDE).
|
||||||
Nodes []Node
|
tokens []token
|
||||||
Footnotes Footnotes
|
Nodes []Node
|
||||||
Outline Outline
|
Footnotes Footnotes
|
||||||
MaxEmphasisNewLines int
|
Outline Outline // Outline is a Table Of Contents for the document and contains all sections (headline + content).
|
||||||
AutoLink bool
|
BufferSettings map[string]string // Settings contains all settings that were parsed from keywords.
|
||||||
BufferSettings map[string]string
|
Error error
|
||||||
DefaultSettings map[string]string
|
|
||||||
Error error
|
|
||||||
Log *log.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writer is the interface that is used to export a parsed document into a new format. See Document.Write().
|
||||||
type Writer interface {
|
type Writer interface {
|
||||||
Before(*Document)
|
Before(*Document) // Before is called before any nodes are passed to the writer.
|
||||||
After(*Document)
|
After(*Document) // After is called after all nodes have been passed to the writer.
|
||||||
WriteNodes(...Node)
|
WriteNodes(...Node) // WriteNodes is called with the nodes of the parsed document.
|
||||||
String() string
|
String() string // String is called at the very end to retrieve the final output.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node represents a parsed node of the document. It's an empty interface and can be ignored.
|
||||||
type Node interface{}
|
type Node interface{}
|
||||||
|
|
||||||
type lexFn = func(line string) (t token, ok bool)
|
type lexFn = func(line string) (t token, ok bool)
|
||||||
|
@ -59,17 +78,11 @@ var lexFns = []lexFn{
|
||||||
|
|
||||||
var nilToken = token{"nil", -1, "", nil}
|
var nilToken = token{"nil", -1, "", nil}
|
||||||
|
|
||||||
func NewDocument() *Document {
|
// New returns a new Configuration with (hopefully) sane defaults.
|
||||||
outlineSection := &Section{}
|
func New() *Configuration {
|
||||||
return &Document{
|
return &Configuration{
|
||||||
Footnotes: Footnotes{
|
|
||||||
Title: "Footnotes",
|
|
||||||
Definitions: map[string]*FootnoteDefinition{},
|
|
||||||
},
|
|
||||||
AutoLink: true,
|
AutoLink: true,
|
||||||
MaxEmphasisNewLines: 1,
|
MaxEmphasisNewLines: 1,
|
||||||
Outline: Outline{outlineSection, outlineSection, 0},
|
|
||||||
BufferSettings: map[string]string{},
|
|
||||||
DefaultSettings: map[string]string{
|
DefaultSettings: map[string]string{
|
||||||
"TODO": "TODO | DONE",
|
"TODO": "TODO | DONE",
|
||||||
"EXCLUDE_TAGS": "noexport",
|
"EXCLUDE_TAGS": "noexport",
|
||||||
|
@ -79,6 +92,7 @@ func NewDocument() *Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write is called after with an instance of the Writer interface to export a parsed Document into another format.
|
||||||
func (d *Document) Write(w Writer) (out string, err error) {
|
func (d *Document) Write(w Writer) (out string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if recovered := recover(); recovered != nil {
|
if recovered := recover(); recovered != nil {
|
||||||
|
@ -96,8 +110,20 @@ func (d *Document) Write(w Writer) (out string, err error) {
|
||||||
return w.String(), err
|
return w.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dIn *Document) Parse(input io.Reader) (d *Document) {
|
// Parse parses the input into an AST (and some other helpful fields like Outline).
|
||||||
d = dIn
|
// To allow method chaining, errors are stored in document.Error rather than being returned.
|
||||||
|
func (c *Configuration) Parse(input io.Reader, path string) (d *Document) {
|
||||||
|
outlineSection := &Section{}
|
||||||
|
d = &Document{
|
||||||
|
Configuration: c,
|
||||||
|
Footnotes: Footnotes{
|
||||||
|
Title: "Footnotes",
|
||||||
|
Definitions: map[string]*FootnoteDefinition{},
|
||||||
|
},
|
||||||
|
Outline: Outline{outlineSection, outlineSection, 0},
|
||||||
|
BufferSettings: map[string]string{},
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if recovered := recover(); recovered != nil {
|
if recovered := recover(); recovered != nil {
|
||||||
d.Error = fmt.Errorf("could not parse input: %v", recovered)
|
d.Error = fmt.Errorf("could not parse input: %v", recovered)
|
||||||
|
@ -112,15 +138,10 @@ func (dIn *Document) Parse(input io.Reader) (d *Document) {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) SetPath(path string) *Document {
|
// Silent disables all logging of warnings during parsing.
|
||||||
d.Path = path
|
func (c *Configuration) Silent() *Configuration {
|
||||||
d.Log.SetPrefix(fmt.Sprintf("%s(%s): ", d.Log.Prefix(), path))
|
c.Log = log.New(ioutil.Discard, "", 0)
|
||||||
return d
|
return c
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Document) Silent() *Document {
|
|
||||||
d.Log = log.New(ioutil.Discard, "", 0)
|
|
||||||
return d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) tokenize(input io.Reader) {
|
func (d *Document) tokenize(input io.Reader) {
|
||||||
|
@ -134,6 +155,7 @@ func (d *Document) tokenize(input io.Reader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the value for key in BufferSettings or DefaultSettings if key does not exist in the former
|
||||||
func (d *Document) Get(key string) string {
|
func (d *Document) Get(key string) string {
|
||||||
if v, ok := d.BufferSettings[key]; ok {
|
if v, ok := d.BufferSettings[key]; ok {
|
||||||
return v
|
return v
|
||||||
|
@ -144,7 +166,15 @@ func (d *Document) Get(key string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// see https://orgmode.org/manual/Export-settings.html
|
// GetOption returns the value associated to the export option key
|
||||||
|
// Currently supported options:
|
||||||
|
// - e (export org entities)
|
||||||
|
// - f (export footnotes)
|
||||||
|
// - toc (export table of content)
|
||||||
|
// - todo (export headline todo status)
|
||||||
|
// - pri (export headline priority)
|
||||||
|
// - tags (export headline tags)
|
||||||
|
// see https://orgmode.org/manual/Export-settings.html for more information
|
||||||
func (d *Document) GetOption(key string) bool {
|
func (d *Document) GetOption(key string) bool {
|
||||||
get := func(settings map[string]string) string {
|
get := func(settings map[string]string) string {
|
||||||
for _, field := range strings.Fields(settings["OPTIONS"]) {
|
for _, field := range strings.Fields(settings["OPTIONS"]) {
|
||||||
|
|
|
@ -9,7 +9,8 @@ import (
|
||||||
|
|
||||||
// Fuzz function to be used by https://github.com/dvyukov/go-fuzz
|
// Fuzz function to be used by https://github.com/dvyukov/go-fuzz
|
||||||
func Fuzz(input []byte) int {
|
func Fuzz(input []byte) int {
|
||||||
d := NewDocument().Silent().Parse(bytes.NewReader(input))
|
conf := New().Silent()
|
||||||
|
d := conf.Parse(bytes.NewReader(input), "")
|
||||||
orgOutput, err := d.Write(NewOrgWriter())
|
orgOutput, err := d.Write(NewOrgWriter())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -18,7 +19,7 @@ func Fuzz(input []byte) int {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
htmlOutputB, err := NewDocument().Silent().Parse(strings.NewReader(orgOutput)).Write(NewHTMLWriter())
|
htmlOutputB, err := conf.Parse(strings.NewReader(orgOutput)).Write(NewHTMLWriter())
|
||||||
if htmlOutputA != htmlOutputB {
|
if htmlOutputA != htmlOutputB {
|
||||||
panic("rendered org results in different html than original input")
|
panic("rendered org results in different html than original input")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"golang.org/x/net/html/atom"
|
"golang.org/x/net/html/atom"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HTMLWriter exports an org document into a html document.
|
||||||
type HTMLWriter struct {
|
type HTMLWriter struct {
|
||||||
stringBuilder
|
stringBuilder
|
||||||
document *Document
|
document *Document
|
||||||
|
|
|
@ -9,7 +9,7 @@ func TestHTMLWriter(t *testing.T) {
|
||||||
for _, path := range orgTestFiles() {
|
for _, path := range orgTestFiles() {
|
||||||
expected := fileString(path[:len(path)-len(".org")] + ".html")
|
expected := fileString(path[:len(path)-len(".org")] + ".html")
|
||||||
reader, writer := strings.NewReader(fileString(path)), NewHTMLWriter()
|
reader, writer := strings.NewReader(fileString(path)), NewHTMLWriter()
|
||||||
actual, err := NewDocument().Silent().SetPath(path).Parse(reader).Write(writer)
|
actual, err := New().Silent().Parse(reader, path).Write(writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s\n got error: %s", path, err)
|
t.Errorf("%s\n got error: %s", path, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -148,7 +148,7 @@ func (d *Document) loadSetupFile(k Keyword) (int, Node) {
|
||||||
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
||||||
return 1, k
|
return 1, k
|
||||||
}
|
}
|
||||||
setupDocument := NewDocument().Parse(bytes.NewReader(bs))
|
setupDocument := d.Configuration.Parse(bytes.NewReader(bs), path)
|
||||||
if err := setupDocument.Error; err != nil {
|
if err := setupDocument.Error; err != nil {
|
||||||
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
||||||
return 1, k
|
return 1, k
|
||||||
|
|
|
@ -9,8 +9,9 @@ import (
|
||||||
|
|
||||||
type stringBuilder = strings.Builder
|
type stringBuilder = strings.Builder
|
||||||
|
|
||||||
|
// OrgWriter export an org document into pretty printed org document.
|
||||||
type OrgWriter struct {
|
type OrgWriter struct {
|
||||||
TagsColumn int // see org-tags-column
|
TagsColumn int
|
||||||
stringBuilder
|
stringBuilder
|
||||||
indent string
|
indent string
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ func TestOrgWriter(t *testing.T) {
|
||||||
for _, path := range orgTestFiles() {
|
for _, path := range orgTestFiles() {
|
||||||
expected := fileString(path[:len(path)-len(".org")] + ".pretty_org")
|
expected := fileString(path[:len(path)-len(".org")] + ".pretty_org")
|
||||||
reader, writer := strings.NewReader(fileString(path)), NewOrgWriter()
|
reader, writer := strings.NewReader(fileString(path)), NewOrgWriter()
|
||||||
actual, err := NewDocument().Silent().SetPath(path).Parse(reader).Write(writer)
|
actual, err := New().Silent().Parse(reader, path).Write(writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s\n got error: %s", path, err)
|
t.Errorf("%s\n got error: %s", path, err)
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue