From 9f7e8a8fbd20c8fe66a5a1c7fa3ccb068f09a983 Mon Sep 17 00:00:00 2001 From: Niklas Fasching Date: Fri, 17 Apr 2020 16:41:06 +0200 Subject: [PATCH] Implement #+MACRO --- org/document.go | 2 ++ org/html_writer.go | 13 +++++++++++++ org/inline.go | 16 ++++++++++++++++ org/keyword.go | 5 +++++ org/org_writer.go | 4 ++++ org/testdata/inline.html | 8 ++++++++ org/testdata/inline.org | 2 ++ org/testdata/inline.pretty_org | 2 ++ org/writer.go | 3 +++ 9 files changed, 55 insertions(+) diff --git a/org/document.go b/org/document.go index 9c464e1..510fe51 100644 --- a/org/document.go +++ b/org/document.go @@ -36,6 +36,7 @@ type Document struct { Path string // Path of the file containing the parse input - used to resolve relative paths during parsing (e.g. INCLUDE). tokens []token baseLvl int + Macros map[string]string Links map[string]string Nodes []Node NamedNodes map[string]Node @@ -123,6 +124,7 @@ func (c *Configuration) Parse(input io.Reader, path string) (d *Document) { BufferSettings: map[string]string{}, NamedNodes: map[string]Node{}, Links: map[string]string{}, + Macros: map[string]string{}, Path: path, } defer func() { diff --git a/org/html_writer.go b/org/html_writer.go index e0290b1..6c230a8 100644 --- a/org/html_writer.go +++ b/org/html_writer.go @@ -355,6 +355,19 @@ func (w *HTMLWriter) WriteRegularLink(l RegularLink) { } } +func (w *HTMLWriter) WriteMacro(m Macro) { + if macro := w.document.Macros[m.Name]; macro != "" { + for i, param := range m.Parameters { + macro = strings.Replace(macro, fmt.Sprintf("$%d", i+1), param, -1) + } + macroDocument := w.document.Parse(strings.NewReader(macro), w.document.Path) + if macroDocument.Error != nil { + w.log.Printf("bad macro: %s -> %s: %v", m.Name, macro, macroDocument.Error) + } + WriteNodes(w, macroDocument.Nodes...) + } +} + func (w *HTMLWriter) WriteList(l List) { tags, ok := listTags[l.Kind] if !ok { diff --git a/org/inline.go b/org/inline.go index 566af0f..ede3355 100644 --- a/org/inline.go +++ b/org/inline.go @@ -54,6 +54,11 @@ type RegularLink struct { AutoLink bool } +type Macro struct { + Name string + Parameters []string +} + var validURLCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=" var autolinkProtocols = regexp.MustCompile(`^(https?|ftp|file)$`) var imageExtensionRegexp = regexp.MustCompile(`^[.](png|gif|jpe?g|svg|tiff?)$`) @@ -66,6 +71,7 @@ var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`) var latexFragmentRegexp = regexp.MustCompile(`(?s)^\\begin{(\w+)}(.*)\\end{(\w+)}`) var inlineBlockRegexp = regexp.MustCompile(`src_(\w+)(\[(.*)\])?{(.*)}`) var inlineExportBlockRegexp = regexp.MustCompile(`@@(\w+):(.*?)@@`) +var macroRegexp = regexp.MustCompile(`{{{(.*)\((.*)\)}}}`) var timestampFormat = "2006-01-02 Mon 15:04" var datestampFormat = "2006-01-02 Mon" @@ -94,6 +100,8 @@ func (d *Document) parseInline(input string) (nodes []Node) { consumed, node = d.parseEmphasis(input, current, true) case '[': consumed, node = d.parseOpeningBracket(input, current) + case '{': + consumed, node = d.parseMacro(input, current) case '<': consumed, node = d.parseTimestamp(input, current) case '\\': @@ -238,6 +246,13 @@ func (d *Document) parseOpeningBracket(input string, start int) (int, Node) { return 0, nil } +func (d *Document) parseMacro(input string, start int) (int, Node) { + if m := macroRegexp.FindStringSubmatch(input[start:]); m != nil { + return len(m[0]), Macro{m[1], strings.Split(m[2], ",")} + } + return 0, nil +} + func (d *Document) parseFootnoteReference(input string, start int) (int, Node) { if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil { name, definition := m[1], m[3] @@ -389,4 +404,5 @@ func (n InlineBlock) String() string { return orgWriter.WriteNodesAsString func (n LatexFragment) String() string { return orgWriter.WriteNodesAsString(n) } func (n FootnoteLink) String() string { return orgWriter.WriteNodesAsString(n) } func (n RegularLink) String() string { return orgWriter.WriteNodesAsString(n) } +func (n Macro) String() string { return orgWriter.WriteNodesAsString(n) } func (n Timestamp) String() string { return orgWriter.WriteNodesAsString(n) } diff --git a/org/keyword.go b/org/keyword.go index 774e9c0..d4bd233 100644 --- a/org/keyword.go +++ b/org/keyword.go @@ -67,6 +67,11 @@ func (d *Document) parseKeyword(i int, stop stopFn) (int, Node) { d.Links[parts[0]] = parts[1] } return 1, k + case "MACRO": + if parts := strings.Split(k.Value, " "); len(parts) >= 2 { + d.Macros[parts[0]] = parts[1] + } + return 1, k case "CAPTION", "ATTR_HTML": consumed, node := d.parseAffiliated(i, stop) if consumed != 0 { diff --git a/org/org_writer.go b/org/org_writer.go index e22a40c..4b4d2a4 100644 --- a/org/org_writer.go +++ b/org/org_writer.go @@ -360,3 +360,7 @@ func (w *OrgWriter) WriteRegularLink(l RegularLink) { w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, w.WriteNodesAsString(l.Description...))) } } + +func (w *OrgWriter) WriteMacro(m Macro) { + w.WriteString(fmt.Sprintf("{{{%s(%s)}}}", m.Name, strings.Join(m.Parameters, ","))) +} diff --git a/org/testdata/inline.html b/org/testdata/inline.html index 89afa2f..74ef1c4 100644 --- a/org/testdata/inline.html +++ b/org/testdata/inline.html @@ -168,6 +168,14 @@ timestamps
  • #+LINK based links: https://www.example.com/foobar +

    +
  • +
  • +

    +#+MACROs:

    +

    yolo

    +

    +

  • diff --git a/org/testdata/inline.org b/org/testdata/inline.org index e2a075f..188e38c 100644 --- a/org/testdata/inline.org +++ b/org/testdata/inline.org @@ -39,3 +39,5 @@ - <2019-01-06 18:00 +1w> - =#+LINK= based links: [[example:foobar]] #+LINK: example https://www.example.com/ +- =#+MACROs=: {{{headline(yolo)}}} + #+MACRO: headline @@html:

    $1

    @@ diff --git a/org/testdata/inline.pretty_org b/org/testdata/inline.pretty_org index 695c39a..60fe598 100644 --- a/org/testdata/inline.pretty_org +++ b/org/testdata/inline.pretty_org @@ -39,3 +39,5 @@ - <2019-01-06 Sun 18:00 +1w> - =#+LINK= based links: [[example:foobar]] #+LINK: example https://www.example.com/ +- =#+MACROs=: {{{headline(yolo)}}} + #+MACRO: headline @@html:

    $1

    @@ diff --git a/org/writer.go b/org/writer.go index 9a50237..29b64bd 100644 --- a/org/writer.go +++ b/org/writer.go @@ -36,6 +36,7 @@ type Writer interface { WriteExplicitLineBreak(ExplicitLineBreak) WriteLineBreak(LineBreak) WriteRegularLink(RegularLink) + WriteMacro(Macro) WriteTimestamp(Timestamp) WriteFootnoteLink(FootnoteLink) WriteFootnoteDefinition(FootnoteDefinition) @@ -95,6 +96,8 @@ func WriteNodes(w Writer, nodes ...Node) { w.WriteLineBreak(n) case RegularLink: w.WriteRegularLink(n) + case Macro: + w.WriteMacro(n) case Timestamp: w.WriteTimestamp(n) case FootnoteLink: