diff --git a/README.org b/README.org index a1f5d00..4eec00e 100644 --- a/README.org +++ b/README.org @@ -1,5 +1,44 @@ -* go-org-orgwiki +* go-org +An Org mode parser and static site generator in go. +Take a look at github pages +- for [[https://niklasfasching.github.io/go-org/][org to html conversion]] examples +- for a [[https://niklasfasching.github.io/go-org/blorg][static site]] generated by blorg +- to [[https://niklasfasching.github.io/go-org/convert.html][try it out live]] in your browser -Thanks to: [[https://github.com/niklasfasching/go-org][go-org]] +[[https://raw.githubusercontent.com/niklasfasching/go-org/master/etc/example.png]] -This fork is used exclusively by [[https://git.fz0x1.wtf/fz0x1/orgwiki][orgwiki]] and includes a few minor modifications tailored for it. +Please note +- the goal for the html export is to produce sensible html output, not to exactly reproduce the output of =org-html-export=. +- the goal for the parser is to support a reasonable subset of Org mode. Org mode is *huge* and I like to follow the 80/20 rule. +* usage +** command line +#+begin_src bash +$ go-org +Usage: go-org COMMAND [ARGS]... +Commands: +- render [FILE] FORMAT + FORMAT: org, html, html-chroma + Instead of specifying a file, org mode content can also be passed on stdin +- blorg + - blorg init + - blorg build + - blorg serve +#+end_src +** as a library +see [[https://github.com/niklasfasching/go-org/blob/master/main.go][main.go]] and hugo [[https://github.com/gohugoio/hugo/blob/master/markup/org/convert.go][org/convert.go]] +* development +1. =make setup= +2. change things +3. =make preview= (regenerates fixtures & shows output in a browser) + +in general, have a look at the Makefile - it's short enough. +* resources +- test files + - [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/site/content-org/all-posts.org][ox-hugo all-posts.org]] + - https://ox-hugo.scripter.co/doc/examples/ +- https://orgmode.org/manual/ +- https://orgmode.org/worg/dev/org-syntax.html +- https://code.orgmode.org/bzg/org-mode/src/master/lisp/org.el + - https://code.orgmode.org/bzg/org-mode/src/master/lisp/org-element.el + - mostly those & ox-html.el, but yeah, all of [[https://code.orgmode.org/bzg/org-mode/src/master/lisp/]] +- existing Org mode implementations: [[https://github.com/emacsmirror/org][org]], [[https://github.com/bdewey/org-ruby/blob/master/spec/html_examples][org-ruby]], [[https://github.com/chaseadamsio/goorgeous/][goorgeous]], [[https://github.com/jgm/pandoc/][pandoc]] diff --git a/etc/generate-gh-pages b/etc/generate-gh-pages index e9c7cee..9bd8cfc 100755 --- a/etc/generate-gh-pages +++ b/etc/generate-gh-pages @@ -133,7 +133,7 @@ echo "$index" > docs/index.html echo "$convert" > docs/convert.html cp etc/_wasm.go docs/wasm.go GOOS=js GOARCH=wasm go build -o docs/main.wasm docs/wasm.go -cp $(go env GOROOT)/lib/wasm/wasm_exec.js docs/wasm_exec.js +cp $(go env GOROOT)/misc/wasm/wasm_exec.js docs/wasm_exec.js mkdir -p docs/blorg cp -r blorg/testdata/public/* docs/blorg/ diff --git a/go.mod b/go.mod index 1ed69ee..0ccf0ce 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,11 @@ module github.com/niklasfasching/go-org -go 1.23.0 - -toolchain go1.24.2 +go 1.18 require ( github.com/alecthomas/chroma/v2 v2.5.0 github.com/pmezard/go-difflib v1.0.0 - golang.org/x/net v0.38.0 + golang.org/x/net v0.23.0 ) require github.com/dlclark/regexp2 v1.4.0 // indirect diff --git a/go.sum b/go.sum index ed71d32..78a9162 100644 --- a/go.sum +++ b/go.sum @@ -7,5 +7,5 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= diff --git a/org/block.go b/org/block.go index 92469f6..6907eaa 100644 --- a/org/block.go +++ b/org/block.go @@ -157,15 +157,7 @@ func splitParameters(s string) []string { parameters, parts := []string{}, strings.Split(s, " :") lang, rest := strings.TrimSpace(parts[0]), parts[1:] if lang != "" { - xs := strings.Fields(lang) - parameters = append(parameters, xs[0]) - for i := 1; i < len(xs); i++ { - k, v := xs[i], "" - if i+1 < len(xs) && xs[i+1][0] != '-' { - v, i = xs[i+1], i+1 - } - parameters = append(parameters, k, v) - } + parameters = append(parameters, lang) } for _, p := range rest { kv := strings.SplitN(p+" ", " ", 2) diff --git a/org/document.go b/org/document.go index edbd380..8dba04f 100644 --- a/org/document.go +++ b/org/document.go @@ -29,7 +29,6 @@ type Configuration struct { 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. ReadFile func(filename string) ([]byte, error) // ReadFile is used to read e.g. #+INCLUDE files. - ResolveLink func(protocol string, description []Node, link string) Node } // Document contains the parsing results and a pointer to the Configuration. @@ -94,9 +93,6 @@ func New() *Configuration { }, Log: log.New(os.Stderr, "go-org: ", 0), ReadFile: ioutil.ReadFile, - ResolveLink: func(protocol string, description []Node, link string) Node { - return RegularLink{protocol, description, link, false} - }, } } @@ -264,9 +260,7 @@ func (d *Document) parseMany(i int, stop stopFn) (int, []Node) { func (d *Document) addHeadline(headline *Headline) int { current := &Section{Headline: headline} d.Outline.last.add(current) - if !headline.IsExcluded(d) { - d.Outline.count++ - } + d.Outline.count++ d.Outline.last = current return d.Outline.count } diff --git a/org/headline.go b/org/headline.go index b8b7b94..f9a77c7 100644 --- a/org/headline.go +++ b/org/headline.go @@ -23,7 +23,6 @@ type Headline struct { Index int Lvl int Status string - IsComment bool Priority string Properties *PropertyDrawer Title []Node @@ -44,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 := trimFastTags( strings.FieldsFunc(d.Get("TODO"), func(r rune) bool { return unicode.IsSpace(r) || r == '|' }), @@ -60,15 +62,12 @@ func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) { headline.Priority = text[2:3] text = strings.TrimSpace(text[4:]) } - if strings.HasPrefix(text, "COMMENT ") { - headline.IsComment = true - text = strings.TrimPrefix(text, "COMMENT ") - } + if m := tagRegexp.FindStringSubmatch(text); m != nil { text = m[1] headline.Tags = strings.FieldsFunc(m[2], func(r rune) bool { return r == ':' }) } - headline.Index = d.addHeadline(&headline) + headline.Title = d.parseInline(text) stop := func(d *Document, i int) bool { @@ -108,9 +107,6 @@ func (h Headline) ID() string { } func (h Headline) IsExcluded(d *Document) bool { - if h.IsComment { - return true - } for _, excludedTag := range strings.Fields(d.Get("EXCLUDE_TAGS")) { for _, tag := range h.Tags { if tag == excludedTag { diff --git a/org/html_writer.go b/org/html_writer.go index 878e29a..ca9ffb2 100644 --- a/org/html_writer.go +++ b/org/html_writer.go @@ -393,16 +393,16 @@ func (w *HTMLWriter) WriteRegularLink(l RegularLink) { if l.Protocol == "file" { url = url[len("file:"):] } - // if isRelative := l.Protocol == "file" || l.Protocol == ""; isRelative && w.PrettyRelativeLinks { - // if !strings.HasPrefix(url, "/") { - // url = "../" + url - // } - // if strings.HasSuffix(url, ".org") { - // url = strings.TrimSuffix(url, ".org") + "/" - // } - // } else if isRelative && strings.HasSuffix(url, ".org") { - // url = strings.TrimSuffix(url, ".org") + ".html" - // } + if isRelative := l.Protocol == "file" || l.Protocol == ""; isRelative && w.PrettyRelativeLinks { + if !strings.HasPrefix(url, "/") { + url = "../" + url + } + if strings.HasSuffix(url, ".org") { + url = strings.TrimSuffix(url, ".org") + "/" + } + } else if isRelative && strings.HasSuffix(url, ".org") { + url = strings.TrimSuffix(url, ".org") + ".html" + } if prefix := w.document.Links[l.Protocol]; prefix != "" { if tag := strings.TrimPrefix(l.URL, l.Protocol+":"); strings.Contains(prefix, "%s") || strings.Contains(prefix, "%h") { url = html.EscapeString(strings.ReplaceAll(strings.ReplaceAll(prefix, "%s", tag), "%h", u.QueryEscape(tag))) diff --git a/org/inline.go b/org/inline.go index 798d30f..dbd20c6 100644 --- a/org/inline.go +++ b/org/inline.go @@ -73,7 +73,7 @@ var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]*?)(:(.*?))?\]`) var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`) var latexFragmentRegexp = regexp.MustCompile(`(?s)^\\begin{(\w+)}(.*)\\end{(\w+)}`) -var inlineBlockRegexp = regexp.MustCompile(`src_(\w+)(\[([^\]]*)\])?{([^}]*)}`) +var inlineBlockRegexp = regexp.MustCompile(`src_(\w+)(\[(.*)\])?{(.*)}`) var inlineExportBlockRegexp = regexp.MustCompile(`@@(\w+):(.*?)@@`) var macroRegexp = regexp.MustCompile(`{{{(.*)\((.*)\)}}}`) @@ -329,7 +329,7 @@ func (d *Document) parseRegularLink(input string, start int) (int, Node) { if len(linkParts) == 2 { protocol = linkParts[0] } - return consumed, d.ResolveLink(protocol, description, link) + return consumed, RegularLink{protocol, description, link, false} } func (d *Document) parseTimestamp(input string, start int) (int, Node) { diff --git a/org/testdata/headlines.html b/org/testdata/headlines.html index ce09dc1..e598777 100644 --- a/org/testdata/headlines.html +++ b/org/testdata/headlines.html @@ -10,9 +10,9 @@
  • headline with custom status
  • -
  • malformed property drawer +
  • malformed property drawer
  • -
  • level limit for headlines to be included in the table of contents +
  • level limit for headlines to be included in the table of contents
  • @@ -75,40 +75,40 @@ headline with custom status -
    -

    +
    +

    malformed property drawer

    -
    +

    :PROPERTIES: not a property

    :END:

    -
    -

    +
    +

    level limit for headlines to be included in the table of contents

    -
    +

    The toc option allows setting a level limit. For this file we set it to 1 - which means that the following headlines won't be included in the table of contents.

    -
    -

    +
    +

    headline 2 not in toc

    -
    -
    -

    +
    +
    +

    headline 3 not in toc

    -
    -

    +
    +

    anoter headline 2 not in toc

    -
    +

    you get the gist…

    diff --git a/org/testdata/headlines.org b/org/testdata/headlines.org index 2613825..ee02735 100644 --- a/org/testdata/headlines.org +++ b/org/testdata/headlines.org @@ -28,8 +28,6 @@ it's possible to use =#+SETUPFILE= - in this case the setup file contains the fo this headline and it's content are not exported as it is marked with an =EXCLUDE_TAGS= tag. By default =EXCLUDE_TAGS= is just =:noexport:=. -* TODO [#A] COMMENT commented headline -this headline is commented out. see [[https://orgmode.org/manual/Comment-Lines.html][comment lines]] * malformed property drawer :PROPERTIES: not a property diff --git a/org/testdata/headlines.pretty_org b/org/testdata/headlines.pretty_org index 2fc26c2..5c6c940 100644 --- a/org/testdata/headlines.pretty_org +++ b/org/testdata/headlines.pretty_org @@ -28,8 +28,6 @@ it's possible to use =#+SETUPFILE= - in this case the setup file contains the fo this headline and it's content are not exported as it is marked with an =EXCLUDE_TAGS= tag. By default =EXCLUDE_TAGS= is just =:noexport:=. -* TODO [#A] commented headline -this headline is commented out. see [[https://orgmode.org/manual/Comment-Lines.html][comment lines]] * malformed property drawer :PROPERTIES: not a property diff --git a/org/testdata/inline.html b/org/testdata/inline.html index daec3fa..67f5495 100644 --- a/org/testdata/inline.html +++ b/org/testdata/inline.html @@ -18,12 +18,6 @@ also hard line breaks not followed by a newline get ignored, see \\ <h1>hello</h1>
    -

    and this
    -
    -
    -world
    -
    -
  • inline export blocks

    hello

  • multiline emphasis is diff --git a/org/testdata/inline.org b/org/testdata/inline.org index cc0c845..7f8b31d 100644 --- a/org/testdata/inline.org +++ b/org/testdata/inline.org @@ -11,7 +11,7 @@ - links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/ - _underlined_ *bold* =verbatim= ~code~ +strikethrough+ - *bold string with an *asterisk inside* -- inline source blocks like src_html[:eval no]{

    hello

    } and this src_schema[:eval no]{world} +- inline source blocks like src_html[:eval no]{

    hello

    } - inline export blocks @@html:

    hello

    @@ - =multiline emphasis is supported - and respects MaxEmphasisNewLines (default: 1)= diff --git a/org/testdata/inline.pretty_org b/org/testdata/inline.pretty_org index 87238cc..ab77e41 100644 --- a/org/testdata/inline.pretty_org +++ b/org/testdata/inline.pretty_org @@ -11,7 +11,7 @@ - links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/ - _underlined_ *bold* =verbatim= ~code~ +strikethrough+ - *bold string with an *asterisk inside* -- inline source blocks like src_html[:eval no]{

    hello

    } and this src_schema[:eval no]{world} +- inline source blocks like src_html[:eval no]{

    hello

    } - inline export blocks @@html:

    hello

    @@ - =multiline emphasis is supported - and respects MaxEmphasisNewLines (default: 1)= diff --git a/org/testdata/misc.html b/org/testdata/misc.html index 4be094c..6c79fbd 100644 --- a/org/testdata/misc.html +++ b/org/testdata/misc.html @@ -171,8 +171,6 @@ it's possible to use =#+SETUPFILE= - in this case the setup file contains th this headline and it's content are not exported as it is marked with an =EXCLUDE_TAGS= tag. By default =EXCLUDE_TAGS= is just =:noexport:=. -* TODO [#A] COMMENT commented headline -this headline is commented out. see [[https://orgmode.org/manual/Comment-Lines.html][comment lines]] * malformed property drawer :PROPERTIES: not a property