diff --git a/tags_import.go b/tags_import.go index a64c820..186fd8e 100644 --- a/tags_import.go +++ b/tags_import.go @@ -8,16 +8,17 @@ 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) - } + // 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 } @@ -76,6 +77,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 } diff --git a/tags_macro.go b/tags_macro.go index 0e196c6..ef3a46b 100644 --- a/tags_macro.go +++ b/tags_macro.go @@ -18,19 +18,18 @@ 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...) - } - + // 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 } @@ -151,6 +150,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 } diff --git a/template.go b/template.go index f96b3f4..f9c24a5 100644 --- a/template.go +++ b/template.go @@ -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 @@ -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) diff --git a/template_tests/macro.tpl b/template_tests/macro.tpl index f0652f5..526e070 100644 --- a/template_tests/macro.tpl +++ b/template_tests/macro.tpl @@ -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() }} diff --git a/template_tests/macro_transitive.tpl b/template_tests/macro_transitive.tpl new file mode 100644 index 0000000..d6bacea --- /dev/null +++ b/template_tests/macro_transitive.tpl @@ -0,0 +1,2 @@ +{% import "macro_transitive/b.tpl" b -%} +a{{ b() }} \ No newline at end of file diff --git a/template_tests/macro_transitive.tpl.out b/template_tests/macro_transitive.tpl.out new file mode 100644 index 0000000..f2ba8f8 --- /dev/null +++ b/template_tests/macro_transitive.tpl.out @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/template_tests/macro_transitive/b.tpl b/template_tests/macro_transitive/b.tpl new file mode 100644 index 0000000..8793517 --- /dev/null +++ b/template_tests/macro_transitive/b.tpl @@ -0,0 +1,2 @@ +{% import "c.tpl" c %} +{% macro b() export %}b{{ c() }}{% endmacro %} diff --git a/template_tests/macro_transitive/c.tpl b/template_tests/macro_transitive/c.tpl new file mode 100644 index 0000000..6e00396 --- /dev/null +++ b/template_tests/macro_transitive/c.tpl @@ -0,0 +1 @@ +{% macro c() export %}c{% endmacro %} \ No newline at end of file diff --git a/variable.go b/variable.go index 96e047e..180f29a 100644 --- a/variable.go +++ b/variable.go @@ -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 { @@ -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() { @@ -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 {