Skip to content

Commit

Permalink
feat(parser/renderer): add user macro feature
Browse files Browse the repository at this point in the history
  • Loading branch information
odknt committed May 13, 2019
1 parent d593a6e commit 7332870
Show file tree
Hide file tree
Showing 9 changed files with 24,534 additions and 22,254 deletions.
25 changes: 25 additions & 0 deletions pkg/parser/asciidoc-grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ DocumentElement <- !EOF // when reaching EOF, do not try to parse a new document
/ DocumentAttributeDeclaration
/ DocumentAttributeReset
/ TableOfContentsMacro
/ UserMacroBlock
/ Paragraph) {
return element, nil
}
Expand Down Expand Up @@ -548,6 +549,29 @@ TitleElement <- element:(Spaces / Dot / CrossReference / Passthrough / InlineIma
// ------------------------------------------
TableOfContentsMacro <- "toc::[]" NEWLINE

// ------------------------------------------
// Custom Macro
// ------------------------------------------
UserMacroBlock <- name:(UserMacroName) "::" value:(UserMacroValue) attrs:(UserMacroAttributes) {
return types.NewUserMacroBlock(name.(string), value.(string), attrs.(types.ElementAttributes))
}

InlineUserMacro <- name:(UserMacroName) ":" value:(UserMacroValue) attrs:(UserMacroAttributes) {
return types.NewInlineUserMacro(name.(string), value.(string), attrs.(types.ElementAttributes))
}

UserMacroName <- (!URL_SCHEME !"." !":" !"[" !"]" !WS !EOL .)+ {
return string(c.text), nil
}

UserMacroValue <- (!":" !"[" !"]" !EOL .)* {
return string(c.text), nil
}

UserMacroAttributes <- "[" attrs:(GenericAttribute)* "]" {
return types.NewInlineAttributes(attrs.([]interface{}))
}

// ------------------------------------------
// File inclusions
// ------------------------------------------
Expand Down Expand Up @@ -834,6 +858,7 @@ InlineElement <- !EOL !LineBreak
/ Link
/ Passthrough
/ InlineFootnote
/ InlineUserMacro
/ Alphanums
/ QuotedText
/ CrossReference
Expand Down
46,518 changes: 24,264 additions & 22,254 deletions pkg/parser/asciidoc_parser.go

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions pkg/parser/user_macro_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package parser_test

import (
"github.com/bytesparadise/libasciidoc/pkg/parser"
"github.com/bytesparadise/libasciidoc/pkg/types"
. "github.com/onsi/ginkgo"
)

var _ = Describe("user macros", func() {

Context("user macros", func() {

It("user macro block", func() {
actualContent := "git::some/url.git[key1=value1,key2=value2]"
expectedResult := types.UserMacro{
Kind: types.BlockMacro,
Name: "git",
Value: "some/url.git",
Attributes: types.ElementAttributes{
"key1": "value1",
"key2": "value2",
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})

It("inline user macro", func() {
actualContent := "repository: git:some/url.git[key1=value1,key2=value2]"
expectedResult := types.Paragraph{
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
{
types.StringElement{
Content: "repository: ",
},
types.UserMacro{
Kind: types.InlineMacro,
Name: "git",
Value: "some/url.git",
Attributes: types.ElementAttributes{
"key1": "value1",
"key2": "value2",
},
},
},
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})
})
})
14 changes: 14 additions & 0 deletions pkg/renderer/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import (
log "github.com/sirupsen/logrus"
)

// MacroFunc represents a user macro function.
type MacroFunc func(e types.UserMacro) ([]byte, error)

// Context is a custom implementation of the standard golang context.Context interface,
// which carries the types.Document which is being processed
type Context struct {
context context.Context
Document types.Document
options map[string]interface{}
macros map[string]MacroFunc
}

// Wrap wraps the given `ctx` context into a new context which will contain the given `document` document.
Expand All @@ -22,6 +26,7 @@ func Wrap(ctx context.Context, document types.Document, options ...Option) *Cont
context: ctx,
Document: document,
options: make(map[string]interface{}),
macros: make(map[string]MacroFunc),
}
for _, option := range options {
option(result)
Expand Down Expand Up @@ -155,6 +160,15 @@ func (ctx *Context) GetImagesDir() string {
return ""
}

// UserMacro finds and returns a user macro function by specified name.
func (ctx *Context) UserMacro(name string) MacroFunc {
macro, ok := ctx.macros[name]
if ok {
return macro
}
return nil
}

// -----------------------
// context.Context methods
// -----------------------
Expand Down
2 changes: 2 additions & 0 deletions pkg/renderer/html5/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func renderElement(ctx *renderer.Context, element interface{}) ([]byte, error) {
return renderAttributeSubstitution(ctx, e), nil
case types.LineBreak:
return renderLineBreak()
case types.UserMacro:
return renderUserMacro(ctx, e)
case types.SingleLineComment:
return nil, nil // nothing to do
default:
Expand Down
17 changes: 17 additions & 0 deletions pkg/renderer/html5/user_macro.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package html5

import (
"errors"

"github.com/bytesparadise/libasciidoc/pkg/renderer"
"github.com/bytesparadise/libasciidoc/pkg/types"
)

func renderUserMacro(ctx *renderer.Context, cm types.UserMacro) ([]byte, error) {
macro := ctx.UserMacro(cm.Name)
if macro == nil {
return nil, errors.New("Unknown user macro: " + cm.Name)
}

return macro(cm)
}
116 changes: 116 additions & 0 deletions pkg/renderer/html5/user_macro_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package html5_test

import (
"github.com/bytesparadise/libasciidoc/pkg/renderer"
"github.com/bytesparadise/libasciidoc/pkg/types"
. "github.com/onsi/ginkgo"
)

func helloMacro(cm types.UserMacro) ([]byte, error) {
name := cm.Value
if name == "" {
name = "world"
}
prefix := cm.Attributes.GetAsString("prefix")
if prefix == "" {
prefix = "hello"
}
suffix := cm.Attributes.GetAsString("suffix")

content := `<span>` + prefix + ` ` + name + suffix + `</span>`
if cm.Kind == types.BlockMacro {
content = `<div class="helloblock">
<div class="content">
` + content + `
</div>
</div>`
}
return []byte(content), nil
}

var _ = Describe("user macros", func() {

Context("user macros", func() {

It("user macro block", func() {

actualContent := "hello::[]"
expectedResult := `<div class="helloblock">
<div class="content">
<span>hello world</span>
</div>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("user macro block with attribute", func() {

actualContent := `hello::[suffix="!!!!"]`
expectedResult := `<div class="helloblock">
<div class="content">
<span>hello world!!!!</span>
</div>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("user macro block with value", func() {

actualContent := `hello::John Doe[]`
expectedResult := `<div class="helloblock">
<div class="content">
<span>hello John Doe</span>
</div>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("user macro block with value and attributes", func() {

actualContent := `hello::John Doe[prefix="Hi",suffix="!!"]`
expectedResult := `<div class="helloblock">
<div class="content">
<span>Hi John Doe!!</span>
</div>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("inline macro", func() {

actualContent := "AAA hello:[]"
expectedResult := `<div class="paragraph">
<p>AAA <span>hello world</span></p>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("inline macro with attribute", func() {

actualContent := `AAA hello:[suffix="!!!!!"]`
expectedResult := `<div class="paragraph">
<p>AAA <span>hello world!!!!!</span></p>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("inline macro with value", func() {

actualContent := `AAA hello:John Doe[]`
expectedResult := `<div class="paragraph">
<p>AAA <span>hello John Doe</span></p>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

It("inline macro with value and attributes", func() {

actualContent := `AAA hello:John Doe[prefix="Hi",suffix="!!"]`
expectedResult := `<div class="paragraph">
<p>AAA <span>Hi John Doe!!</span></p>
</div>`
verify(GinkgoT(), expectedResult, actualContent, renderer.DefineMacro("hello", helloMacro))
})

})
})
7 changes: 7 additions & 0 deletions pkg/renderer/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ func Entrypoint(entrypoint string) Option {
}
}

// DefineMacro defines the given function to a user macro with the given name
func DefineMacro(name string, cm MacroFunc) Option {
return func(ctx *Context) {
ctx.macros[name] = cm
}
}

// LastUpdated returns the value of the 'LastUpdated' Option if it was present,
// otherwise it returns the current time using the `2006/01/02 15:04:05 MST` format
func (ctx *Context) LastUpdated() string {
Expand Down
38 changes: 38 additions & 0 deletions pkg/types/grammar_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,44 @@ const (
type TableOfContentsMacro struct {
}

// ------------------------------------------
// Custom Macro
// ------------------------------------------

const (
// InlineMacro a inline user macro
InlineMacro UserMacroKind = "inline"
// BlockMacro a block user macro
BlockMacro UserMacroKind = "block"
)

// UserMacroKind the type of user macro
type UserMacroKind string

// UserMacro the structure for Custom Macro
type UserMacro struct {
Kind UserMacroKind
Name string
Value string
Attributes ElementAttributes
}

// NewUserMacroBlock returns an UserMacro
func NewUserMacroBlock(name, value string, attrs ElementAttributes) (UserMacro, error) {
return UserMacro{Name: name, Kind: BlockMacro, Value: value, Attributes: attrs}, nil
}

// AddAttributes adds all given attributes to the current set of attribute of the element
func (d UserMacro) AddAttributes(attributes ElementAttributes) {
d.Attributes.AddAll(attributes)

}

// NewInlineUserMacro returns an UserMacro
func NewInlineUserMacro(name, value string, attrs ElementAttributes) (UserMacro, error) {
return UserMacro{Name: name, Kind: InlineMacro, Value: value, Attributes: attrs}, nil
}

// ------------------------------------------
// Preamble
// ------------------------------------------
Expand Down

0 comments on commit 7332870

Please sign in to comment.