Skip to content

Commit

Permalink
feat: Support page inclusion macro
Browse files Browse the repository at this point in the history
Also generalize the ac_tag_parser a bit and support <ri:* /> tags as
well
  • Loading branch information
mrueg committed Mar 31, 2023
1 parent 943a356 commit 1285947
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 14 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ By default, mark provides several built-in templates and macros:

See: https://confluence.atlassian.com/doc/blog-posts-macro-139470.html

* template: `ac:include` to include a page
- Page: the page to be included
- Space: the space the page is in (optional, otherwise same space)

* macro `@{...}` to mention user by name specified in the braces.

## Template & Macros Usecases
Expand Down
3 changes: 2 additions & 1 deletion pkg/mark/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"regexp"
"strings"

cparser "github.com/kovetskiy/mark/pkg/mark/parser"
"github.com/kovetskiy/mark/pkg/mark/stdlib"
"github.com/reconquest/pkg/log"
"github.com/yuin/goldmark"
Expand Down Expand Up @@ -450,7 +451,7 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib) string {
converter.Parser().AddOptions(parser.WithInlineParsers(
// Must be registered with a higher priority than goldmark's linkParser to make sure goldmark doesn't parse
// the <ac:*/> tags.
util.Prioritized(NewACTagParser(), 199),
util.Prioritized(cparser.NewConfluenceTagParser(), 199),
))

converter.Renderer().AddOptions(renderer.WithNodeRenderers(
Expand Down
26 changes: 13 additions & 13 deletions pkg/mark/ac_tag_parser.go → pkg/mark/parser/confluencetags.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mark
package parser

import (
"bytes"
Expand All @@ -9,25 +9,25 @@ import (
"regexp"
)

// NewACTagParser returns an inline parser that parses <ac:* /> tags to ensure that Confluence specific tags are parsed
// NewConfluenceTagParser returns an inline parser that parses <ac:* /> and <ri:* /> tags to ensure that Confluence specific tags are parsed
// as ast.KindRawHtml so they are not escaped at render time. The parser must be registered with a higher priority
// than goldmark's linkParser. Otherwise, the linkParser would parse the <ac:* /> tags.
func NewACTagParser() parser.InlineParser {
return &acTagParser{}
func NewConfluenceTagParser() parser.InlineParser {
return &confluenceTagParser{}
}

var _ parser.InlineParser = (*acTagParser)(nil)
var _ parser.InlineParser = (*confluenceTagParser)(nil)

// acTagParser is a stripped down version of goldmark's rawHTMLParser.
// confluenceTagParser is a stripped down version of goldmark's rawHTMLParser.
// See: https://github.com/yuin/goldmark/blob/master/parser/raw_html.go
type acTagParser struct {
type confluenceTagParser struct {
}

func (s *acTagParser) Trigger() []byte {
func (s *confluenceTagParser) Trigger() []byte {
return []byte{'<'}
}

func (s *acTagParser) Parse(_ ast.Node, block text.Reader, pc parser.Context) ast.Node {
func (s *confluenceTagParser) Parse(_ ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, _ := block.PeekLine()
if len(line) > 1 && util.IsAlphaNumeric(line[1]) {
return s.parseMultiLineRegexp(openTagRegexp, block, pc)
Expand All @@ -48,15 +48,15 @@ var tagnamePattern = `([A-Za-z][A-Za-z0-9-]*)`

var attributePattern = `(?:[\r\n \t]+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:[\r\n \t]*=[\r\n \t]*(?:[^\"'=<>` + "`" + `\x00-\x20]+|'[^']*'|"[^"]*"))?)`

// Only match <ac:*/> tags
var openTagRegexp = regexp.MustCompile("^<ac:" + tagnamePattern + attributePattern + `*[ \t]*/?>`)
// Only match <ac:*/> and <ri:*/> tags
var openTagRegexp = regexp.MustCompile("^<(ac|ri):" + tagnamePattern + attributePattern + `*[ \t]*/?>`)
var closeTagRegexp = regexp.MustCompile("^</ac:" + tagnamePattern + `\s*>`)

var openCDATA = []byte("<![CDATA[")
var closeCDATA = []byte("]]>")
var closeDecl = []byte(">")

func (s *acTagParser) parseUntil(block text.Reader, closer []byte, _ parser.Context) ast.Node {
func (s *confluenceTagParser) parseUntil(block text.Reader, closer []byte, _ parser.Context) ast.Node {
savedLine, savedSegment := block.Position()
node := ast.NewRawHTML()
for {
Expand All @@ -77,7 +77,7 @@ func (s *acTagParser) parseUntil(block text.Reader, closer []byte, _ parser.Cont
return nil
}

func (s *acTagParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Reader, _ parser.Context) ast.Node {
func (s *confluenceTagParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Reader, _ parser.Context) ast.Node {
sline, ssegment := block.Position()
if block.Match(reg) {
node := ast.NewRawHTML()
Expand Down
12 changes: 12 additions & 0 deletions pkg/mark/stdlib/stdlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ func templates(api *confluence.API) (*template.Template, error) {
`</ac:structured-macro>{{printf "\n"}}`,
),

/* https://confluence.atlassian.com/conf59/include-page-macro-792499125.html */

`ac:include`: text(
`<ac:structured-macro ac:name="include">{{printf "\n"}}`,
`<ac:parameter ac:name="">{{printf "\n"}}`,
`<ac:link>{{printf "\n"}}`,
`<ri:page ri:content-title="{{ .Page }}" {{if .Space }}ri:space-key="{{ .Space }}"{{end}}/>{{printf "\n"}}`,
`</ac:link>{{printf "\n"}}`,
`</ac:parameter>{{printf "\n"}}`,
`</ac:structured-macro>{{printf "\n"}}`,
),

// TODO(seletskiy): more templates here
} {
templates, err = templates.New(name).Parse(body)
Expand Down

0 comments on commit 1285947

Please sign in to comment.