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) {
|
||||
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 {
|
||||
out.Set("innerHTML", fmt.Sprintf("<pre>%s</pre>", err))
|
||||
} else {
|
||||
|
|
2
main.go
2
main.go
|
@ -27,7 +27,7 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
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]) {
|
||||
case "org":
|
||||
out, err = d.Write(org.NewOrgWriter())
|
||||
|
|
|
@ -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
|
||||
|
||||
import (
|
||||
|
@ -10,27 +22,34 @@ import (
|
|||
"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 {
|
||||
Path string
|
||||
*Configuration
|
||||
Path string // Path of the file containing the parse input - used to resolve relative paths during parsing (e.g. INCLUDE).
|
||||
tokens []token
|
||||
Nodes []Node
|
||||
Footnotes Footnotes
|
||||
Outline Outline
|
||||
MaxEmphasisNewLines int
|
||||
AutoLink bool
|
||||
BufferSettings map[string]string
|
||||
DefaultSettings map[string]string
|
||||
Outline Outline // Outline is a Table Of Contents for the document and contains all sections (headline + content).
|
||||
BufferSettings map[string]string // Settings contains all settings that were parsed from keywords.
|
||||
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 {
|
||||
Before(*Document)
|
||||
After(*Document)
|
||||
WriteNodes(...Node)
|
||||
String() string
|
||||
Before(*Document) // Before is called before any nodes are passed to the writer.
|
||||
After(*Document) // After is called after all nodes have been passed to the writer.
|
||||
WriteNodes(...Node) // WriteNodes is called with the nodes of the parsed document.
|
||||
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 lexFn = func(line string) (t token, ok bool)
|
||||
|
@ -59,17 +78,11 @@ var lexFns = []lexFn{
|
|||
|
||||
var nilToken = token{"nil", -1, "", nil}
|
||||
|
||||
func NewDocument() *Document {
|
||||
outlineSection := &Section{}
|
||||
return &Document{
|
||||
Footnotes: Footnotes{
|
||||
Title: "Footnotes",
|
||||
Definitions: map[string]*FootnoteDefinition{},
|
||||
},
|
||||
// New returns a new Configuration with (hopefully) sane defaults.
|
||||
func New() *Configuration {
|
||||
return &Configuration{
|
||||
AutoLink: true,
|
||||
MaxEmphasisNewLines: 1,
|
||||
Outline: Outline{outlineSection, outlineSection, 0},
|
||||
BufferSettings: map[string]string{},
|
||||
DefaultSettings: map[string]string{
|
||||
"TODO": "TODO | DONE",
|
||||
"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) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
|
@ -96,8 +110,20 @@ func (d *Document) Write(w Writer) (out string, err error) {
|
|||
return w.String(), err
|
||||
}
|
||||
|
||||
func (dIn *Document) Parse(input io.Reader) (d *Document) {
|
||||
d = dIn
|
||||
// Parse parses the input into an AST (and some other helpful fields like Outline).
|
||||
// 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() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (d *Document) SetPath(path string) *Document {
|
||||
d.Path = path
|
||||
d.Log.SetPrefix(fmt.Sprintf("%s(%s): ", d.Log.Prefix(), path))
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Document) Silent() *Document {
|
||||
d.Log = log.New(ioutil.Discard, "", 0)
|
||||
return d
|
||||
// Silent disables all logging of warnings during parsing.
|
||||
func (c *Configuration) Silent() *Configuration {
|
||||
c.Log = log.New(ioutil.Discard, "", 0)
|
||||
return c
|
||||
}
|
||||
|
||||
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 {
|
||||
if v, ok := d.BufferSettings[key]; ok {
|
||||
return v
|
||||
|
@ -144,7 +166,15 @@ func (d *Document) Get(key string) string {
|
|||
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 {
|
||||
get := func(settings map[string]string) string {
|
||||
for _, field := range strings.Fields(settings["OPTIONS"]) {
|
||||
|
|
|
@ -9,7 +9,8 @@ import (
|
|||
|
||||
// Fuzz function to be used by https://github.com/dvyukov/go-fuzz
|
||||
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())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -18,7 +19,7 @@ func Fuzz(input []byte) int {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
htmlOutputB, err := NewDocument().Silent().Parse(strings.NewReader(orgOutput)).Write(NewHTMLWriter())
|
||||
htmlOutputB, err := conf.Parse(strings.NewReader(orgOutput)).Write(NewHTMLWriter())
|
||||
if htmlOutputA != htmlOutputB {
|
||||
panic("rendered org results in different html than original input")
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// HTMLWriter exports an org document into a html document.
|
||||
type HTMLWriter struct {
|
||||
stringBuilder
|
||||
document *Document
|
||||
|
|
|
@ -9,7 +9,7 @@ func TestHTMLWriter(t *testing.T) {
|
|||
for _, path := range orgTestFiles() {
|
||||
expected := fileString(path[:len(path)-len(".org")] + ".html")
|
||||
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 {
|
||||
t.Errorf("%s\n got error: %s", path, err)
|
||||
continue
|
||||
|
|
|
@ -148,7 +148,7 @@ func (d *Document) loadSetupFile(k Keyword) (int, Node) {
|
|||
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
||||
return 1, k
|
||||
}
|
||||
setupDocument := NewDocument().Parse(bytes.NewReader(bs))
|
||||
setupDocument := d.Configuration.Parse(bytes.NewReader(bs), path)
|
||||
if err := setupDocument.Error; err != nil {
|
||||
d.Log.Printf("Bad setup file: %#v: %s", k, err)
|
||||
return 1, k
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
|
||||
type stringBuilder = strings.Builder
|
||||
|
||||
// OrgWriter export an org document into pretty printed org document.
|
||||
type OrgWriter struct {
|
||||
TagsColumn int // see org-tags-column
|
||||
TagsColumn int
|
||||
stringBuilder
|
||||
indent string
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func TestOrgWriter(t *testing.T) {
|
|||
for _, path := range orgTestFiles() {
|
||||
expected := fileString(path[:len(path)-len(".org")] + ".pretty_org")
|
||||
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 {
|
||||
t.Errorf("%s\n got error: %s", path, err)
|
||||
continue
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue