Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions tags_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,10 @@ type tagImportNode struct {
position *Token
filename string
macros map[string]*tagMacroNode // alias/name -> macro instance
template *Template
}

func (node *tagImportNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
for name, macro := range node.macros {
func(name string, macro *tagMacroNode) {
ctx.Private[name] = func(args ...*Value) (*Value, error) {
return macro.call(ctx, args...)
}
}(name, macro)
}
return nil
}

Expand Down Expand Up @@ -76,6 +70,9 @@ func tagImportParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *E
}
}

doc.template.macroImportNodes = append(doc.template.macroImportNodes, importNode)
importNode.template = tpl

return importNode, nil
}

Expand Down
15 changes: 2 additions & 13 deletions tags_macro.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@ type tagMacroNode struct {
}

func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
ctx.Private[node.name] = func(args ...*Value) (*Value, error) {
ctx.macroDepth++
defer func() {
ctx.macroDepth--
}()

if ctx.macroDepth > maxMacroDepth {
return nil, ctx.Error(fmt.Sprintf("maximum recursive macro call depth reached (max is %v)", maxMacroDepth), node.position)
}

return node.call(ctx, args...)
}

return nil
}

Expand Down Expand Up @@ -151,6 +138,8 @@ func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Er
doc.template.exportedMacros[macroNode.name] = macroNode
}

doc.template.ownMacroNodes = append(doc.template.ownMacroNodes, macroNode)

return macroNode, nil
}

Expand Down
30 changes: 17 additions & 13 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ type Template struct {
parser *Parser

// first come, first serve (it's important to not override existing entries in here)
level int
parent *Template
child *Template
blocks map[string]*NodeWrapper
exportedMacros map[string]*tagMacroNode
level int
parent *Template
child *Template
blocks map[string]*NodeWrapper
exportedMacros map[string]*tagMacroNode
ownMacroNodes []*tagMacroNode
macroImportNodes []*tagImportNode

// Output
root *nodeDocument
Expand All @@ -61,14 +63,16 @@ func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*

// Create the template
t := &Template{
set: set,
isTplString: isTplString,
name: name,
tpl: strTpl,
size: len(strTpl),
blocks: make(map[string]*NodeWrapper),
exportedMacros: make(map[string]*tagMacroNode),
Options: newOptions(),
set: set,
isTplString: isTplString,
name: name,
tpl: strTpl,
size: len(strTpl),
blocks: make(map[string]*NodeWrapper),
exportedMacros: make(map[string]*tagMacroNode),
ownMacroNodes: make([]*tagMacroNode, 0),
macroImportNodes: make([]*tagImportNode, 0),
Options: newOptions(),
}
// Copy all settings from another Options.
t.Options.Update(set.Options)
Expand Down
4 changes: 2 additions & 2 deletions template_tests/macro.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ issue #39 (deactivate auto-escape of macros)
{{ html_test("Max") }}

Importing macros
{% import "macro.helper" imported_macro, imported_macro as renamed_macro, imported_macro as html_test %}
{% import "macro.helper" imported_macro, imported_macro as renamed_macro, imported_macro as html_test2 %}
{{ imported_macro("User1") }}
{{ renamed_macro("User2") }}
{{ html_test("Max") }}
{{ html_test2("Max") }}

Chaining macros{% import "macro2.helper" greeter_macro %}
{{ greeter_macro() }}
Expand Down
2 changes: 2 additions & 0 deletions template_tests/macro_transitive.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% import "macro_transitive/b.tpl" b -%}
a{{ b() }}
1 change: 1 addition & 0 deletions template_tests/macro_transitive.tpl.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
abc
2 changes: 2 additions & 0 deletions template_tests/macro_transitive/b.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% import "c.tpl" c %}
{% macro b() export %}b{{ c() }}{% endmacro %}
1 change: 1 addition & 0 deletions template_tests/macro_transitive/c.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% macro c() export %}c{% endmacro %}
56 changes: 54 additions & 2 deletions variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,33 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) {
// First we're having a look in our private
// context (e. g. information provided by tags, like the forloop)
val, inPrivate := ctx.Private[vr.parts[0].s]
valPublic, inPublic := ctx.Public[vr.parts[0].s]

if !inPrivate {
// Nothing found? Then have a final lookup in the public context
val = ctx.Public[vr.parts[0].s]
if inPublic {
// Nothing found? Then have a final lookup in the public context
val = valPublic
} else {
// could be a macro
macroNode, template := lookupMacro(ctx.template, vr.parts[0].s)
if macroNode != nil {
val = func(args ...*Value) (*Value, error) {
ctx.macroDepth++
thisTemplate := ctx.template
defer func() {
ctx.macroDepth--
ctx.template = thisTemplate
}()
ctx.template = template

if ctx.macroDepth > maxMacroDepth {
return nil, ctx.Error(fmt.Sprintf("maximum recursive macro call depth reached (max is %v)", maxMacroDepth), macroNode.position)
}

return macroNode.call(ctx, args...)
}
}
}
}
current = reflect.ValueOf(val) // Get the initial value
} else {
Expand Down Expand Up @@ -505,6 +529,7 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) {
current = rv.Interface().(*Value).val
isSafe = rv.Interface().(*Value).safe
}

}

if !current.IsValid() {
Expand All @@ -516,6 +541,33 @@ func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) {
return &Value{val: current, safe: isSafe}, nil
}

// lookupMacro searches for the macro in the current template including imports
// and returns the macro's call function along with the template it is registered in
func lookupMacro(template *Template, name string) (*tagMacroNode, *Template) {
var macro *tagMacroNode
for _, m := range template.ownMacroNodes {
if m.name == name {
macro = m
break
}
}
if macro != nil {
return macro, template
}
for _, importNode := range template.macroImportNodes {
for n, m := range importNode.macros {
if n == name {
macro = m
break
}
}
if macro != nil {
return macro, importNode.template
}
}
return nil, nil
}

func (vr *variableResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
value, err := vr.resolve(ctx)
if err != nil {
Expand Down