From 1285947ab3fc8f414038d697e7055d2242fb0990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20R=C3=BCger?= Date: Fri, 31 Mar 2023 16:25:54 +0200 Subject: [PATCH] feat: Support page inclusion macro Also generalize the ac_tag_parser a bit and support tags as well --- README.md | 4 +++ pkg/mark/markdown.go | 3 ++- .../confluencetags.go} | 26 +++++++++---------- pkg/mark/stdlib/stdlib.go | 12 +++++++++ 4 files changed, 31 insertions(+), 14 deletions(-) rename pkg/mark/{ac_tag_parser.go => parser/confluencetags.go} (72%) diff --git a/README.md b/README.md index c8f3b8f..3ce0f18 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/pkg/mark/markdown.go b/pkg/mark/markdown.go index 680375d..5b12cd1 100644 --- a/pkg/mark/markdown.go +++ b/pkg/mark/markdown.go @@ -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" @@ -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 tags. - util.Prioritized(NewACTagParser(), 199), + util.Prioritized(cparser.NewConfluenceTagParser(), 199), )) converter.Renderer().AddOptions(renderer.WithNodeRenderers( diff --git a/pkg/mark/ac_tag_parser.go b/pkg/mark/parser/confluencetags.go similarity index 72% rename from pkg/mark/ac_tag_parser.go rename to pkg/mark/parser/confluencetags.go index 763a1ca..56bba8d 100644 --- a/pkg/mark/ac_tag_parser.go +++ b/pkg/mark/parser/confluencetags.go @@ -1,4 +1,4 @@ -package mark +package parser import ( "bytes" @@ -9,25 +9,25 @@ import ( "regexp" ) -// NewACTagParser returns an inline parser that parses tags to ensure that Confluence specific tags are parsed +// NewConfluenceTagParser returns an inline parser that parses and 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 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) @@ -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 tags -var openTagRegexp = regexp.MustCompile("^`) +// Only match and tags +var openTagRegexp = regexp.MustCompile("^<(ac|ri):" + tagnamePattern + attributePattern + `*[ \t]*/?>`) var closeTagRegexp = regexp.MustCompile("^`) var openCDATA = []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 { @@ -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() diff --git a/pkg/mark/stdlib/stdlib.go b/pkg/mark/stdlib/stdlib.go index 31c20b6..62daf5f 100644 --- a/pkg/mark/stdlib/stdlib.go +++ b/pkg/mark/stdlib/stdlib.go @@ -261,6 +261,18 @@ func templates(api *confluence.API) (*template.Template, error) { `{{printf "\n"}}`, ), + /* https://confluence.atlassian.com/conf59/include-page-macro-792499125.html */ + + `ac:include`: text( + `{{printf "\n"}}`, + `{{printf "\n"}}`, + `{{printf "\n"}}`, + `{{printf "\n"}}`, + `{{printf "\n"}}`, + `{{printf "\n"}}`, + `{{printf "\n"}}`, + ), + // TODO(seletskiy): more templates here } { templates, err = templates.New(name).Parse(body)