Compare commits

...

11 commits

Author SHA1 Message Date
b9f94aafed
orgwiki patch
Some checks failed
CI / build (push) Has been cancelled
2025-06-23 15:15:12 +02:00
Niklas Fasching
ed88fea76d Fix generate-gh-pages: Apparently wasm_exec.js was moved 2025-05-12 22:45:04 +02:00
Niklas Fasching
6d8701e84d
Merge pull request #121 from theSuess/master
feat: allow for custom link resolving
2025-05-12 19:51:31 +02:00
Niklas Fasching
f8bd23d282
Merge pull request #123 from niklasfasching/dependabot/go_modules/golang.org/x/net-0.38.0
build(deps): bump golang.org/x/net from 0.23.0 to 0.38.0
2025-05-12 19:50:36 +02:00
dependabot[bot]
bb5515f225
build(deps): bump golang.org/x/net from 0.23.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.23.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 21:50:11 +00:00
Dominik Süß
417b814e85
feat: allow for custom link resolving 2025-01-02 11:40:02 +01:00
Niklas Fasching
075f3bab6d
Merge pull request #115 from Gusted/multiple-inline
Allow for multiple inline blocks in a paragraph
2024-09-02 13:50:17 +02:00
Niklas Fasching
8e4a992dbd Parse switches in code block params
There's has a special case for parameters that start with `-` and come directly
after the language. See [1]

[1] https://orgmode.org/manual/Literal-Examples.html#FOOT117
2024-08-20 15:56:53 +02:00
Niklas Fasching
6cfae31132 Add support for COMMENT headlines
https://orgmode.org/manual/Comment-Lines.html
2024-08-20 15:26:31 +02:00
Niklas Fasching
7bc6100fc7 Do not include excluded headlines when calculating headline index 2024-08-20 15:23:42 +02:00
Gusted
f537c0bda3
Allow for multiple inline blocks in a paragraph
- Adjust the inlineBlock regex to match until the first `]` and `}`,
this allows for multiple inline blocks to be present on the same line.
- Test added.
2024-07-29 17:49:02 +02:00
16 changed files with 77 additions and 84 deletions

View file

@ -1,44 +1,5 @@
* 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
* go-org-orgwiki
[[https://raw.githubusercontent.com/niklasfasching/go-org/master/etc/example.png]]
Thanks to: [[https://github.com/niklasfasching/go-org][go-org]]
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]]
This fork is used exclusively by [[https://git.fz0x1.wtf/fz0x1/orgwiki][orgwiki]] and includes a few minor modifications tailored for it.

View file

@ -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)/misc/wasm/wasm_exec.js docs/wasm_exec.js
cp $(go env GOROOT)/lib/wasm/wasm_exec.js docs/wasm_exec.js
mkdir -p docs/blorg
cp -r blorg/testdata/public/* docs/blorg/

6
go.mod
View file

@ -1,11 +1,13 @@
module github.com/niklasfasching/go-org
go 1.18
go 1.23.0
toolchain go1.24.2
require (
github.com/alecthomas/chroma/v2 v2.5.0
github.com/pmezard/go-difflib v1.0.0
golang.org/x/net v0.23.0
golang.org/x/net v0.38.0
)
require github.com/dlclark/regexp2 v1.4.0 // indirect

4
go.sum
View file

@ -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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=

View file

@ -157,7 +157,15 @@ func splitParameters(s string) []string {
parameters, parts := []string{}, strings.Split(s, " :")
lang, rest := strings.TrimSpace(parts[0]), parts[1:]
if lang != "" {
parameters = append(parameters, 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)
}
}
for _, p := range rest {
kv := strings.SplitN(p+" ", " ", 2)

View file

@ -29,6 +29,7 @@ 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.
@ -93,6 +94,9 @@ 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}
},
}
}
@ -260,7 +264,9 @@ 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)
d.Outline.count++
if !headline.IsExcluded(d) {
d.Outline.count++
}
d.Outline.last = current
return d.Outline.count
}

View file

@ -23,6 +23,7 @@ type Headline struct {
Index int
Lvl int
Status string
IsComment bool
Priority string
Properties *PropertyDrawer
Title []Node
@ -43,9 +44,6 @@ 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 == '|' }),
@ -62,12 +60,15 @@ 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 {
@ -107,6 +108,9 @@ 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 {

View file

@ -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)))

View file

@ -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, RegularLink{protocol, description, link, false}
return consumed, d.ResolveLink(protocol, description, link)
}
func (d *Document) parseTimestamp(input string, start int) (int, Node) {

View file

@ -10,9 +10,9 @@
</li>
<li><a href="#headline-5">headline with custom status</a>
</li>
<li><a href="#headline-7">malformed property drawer</a>
<li><a href="#headline-6">malformed property drawer</a>
</li>
<li><a href="#headline-8">level limit for headlines to be included in the table of contents</a>
<li><a href="#headline-7">level limit for headlines to be included in the table of contents</a>
</li>
</ul>
</nav>
@ -75,40 +75,40 @@ headline with custom status
</div>
</div>
</div>
<div id="outline-container-headline-7" class="outline-2">
<h2 id="headline-7">
<div id="outline-container-headline-6" class="outline-2">
<h2 id="headline-6">
malformed property drawer
</h2>
<div id="outline-text-headline-7" class="outline-text-2">
<div id="outline-text-headline-6" class="outline-text-2">
<p>:PROPERTIES:
not a property</p>
<p>:END:</p>
</div>
</div>
<div id="outline-container-headline-8" class="outline-2">
<h2 id="headline-8">
<div id="outline-container-headline-7" class="outline-2">
<h2 id="headline-7">
level limit for headlines to be included in the table of contents
</h2>
<div id="outline-text-headline-8" class="outline-text-2">
<div id="outline-text-headline-7" class="outline-text-2">
<p>The toc option allows setting a <a href="https://orgmode.org/manual/Export-settings.html">level limit</a>. For this file we set it to 1 - which means that the following headlines
won&#39;t be included in the table of contents.</p>
<div id="outline-container-headline-9" class="outline-3">
<h3 id="headline-9">
<div id="outline-container-headline-8" class="outline-3">
<h3 id="headline-8">
headline 2 not in toc
</h3>
<div id="outline-text-headline-9" class="outline-text-3">
<div id="outline-container-headline-10" class="outline-4">
<h4 id="headline-10">
<div id="outline-text-headline-8" class="outline-text-3">
<div id="outline-container-headline-9" class="outline-4">
<h4 id="headline-9">
headline 3 not in toc
</h4>
</div>
</div>
</div>
<div id="outline-container-headline-11" class="outline-3">
<h3 id="headline-11">
<div id="outline-container-headline-10" class="outline-3">
<h3 id="headline-10">
anoter headline 2 not in toc
</h3>
<div id="outline-text-headline-11" class="outline-text-3">
<div id="outline-text-headline-10" class="outline-text-3">
<p>you get the gist…</p>
</div>
</div>

View file

@ -28,6 +28,8 @@ 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

View file

@ -28,6 +28,8 @@ 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

View file

@ -18,6 +18,12 @@ also hard line breaks not followed by a newline get ignored, see \\</li>
&lt;h1&gt;hello&lt;/h1&gt;
</pre>
</div>
</div> and this <div class="src src-inline src-schema">
<div class="highlight-inline">
<pre>
world
</pre>
</div>
</div></li>
<li>inline export blocks <h1>hello</h1></li>
<li><code class="verbatim">multiline emphasis is

View file

@ -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]{<h1>hello</h1>}
- inline source blocks like src_html[:eval no]{<h1>hello</h1>} and this src_schema[:eval no]{world}
- inline export blocks @@html:<h1>hello</h1>@@
- =multiline emphasis is
supported - and respects MaxEmphasisNewLines (default: 1)=

View file

@ -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]{<h1>hello</h1>}
- inline source blocks like src_html[:eval no]{<h1>hello</h1>} and this src_schema[:eval no]{world}
- inline export blocks @@html:<h1>hello</h1>@@
- =multiline emphasis is
supported - and respects MaxEmphasisNewLines (default: 1)=

View file

@ -171,6 +171,8 @@ it&#39;s possible to use =#+SETUPFILE= - in this case the setup file contains th
this headline and it&#39;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