Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/internal/go_templates/texttemplate/hugo_template.go')
-rw-r--r--tpl/internal/go_templates/texttemplate/hugo_template.go104
1 files changed, 98 insertions, 6 deletions
diff --git a/tpl/internal/go_templates/texttemplate/hugo_template.go b/tpl/internal/go_templates/texttemplate/hugo_template.go
index be8a5558f..a39f027fb 100644
--- a/tpl/internal/go_templates/texttemplate/hugo_template.go
+++ b/tpl/internal/go_templates/texttemplate/hugo_template.go
@@ -34,8 +34,9 @@ type Preparer interface {
// ExecHelper allows some custom eval hooks.
type ExecHelper interface {
- GetFunc(name string) (reflect.Value, bool)
- GetMapValue(receiver, key reflect.Value) (reflect.Value, bool)
+ GetFunc(tmpl Preparer, name string) (reflect.Value, bool)
+ GetMethod(tmpl Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value)
+ GetMapValue(tmpl Preparer, receiver, key reflect.Value) (reflect.Value, bool)
}
// Executer executes a given template.
@@ -64,6 +65,7 @@ func (t *executer) Execute(p Preparer, wr io.Writer, data interface{}) error {
state := &state{
helper: t.helper,
+ prep: p,
tmpl: tmpl,
wr: wr,
vars: []variable{{"$", value}},
@@ -75,7 +77,6 @@ func (t *executer) Execute(p Preparer, wr io.Writer, data interface{}) error {
// Prepare returns a template ready for execution.
func (t *Template) Prepare() (*Template, error) {
-
return t, nil
}
@@ -95,6 +96,7 @@ func (t *Template) executeWithState(state *state, value reflect.Value) (err erro
// can execute in parallel.
type state struct {
tmpl *Template
+ prep Preparer // Added for Hugo.
helper ExecHelper // Added for Hugo.
wr io.Writer
node parse.Node // current node, for errors
@@ -110,7 +112,7 @@ func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd
var ok bool
if s.helper != nil {
// Added for Hugo.
- function, ok = s.helper.GetFunc(name)
+ function, ok = s.helper.GetFunc(s.prep, name)
}
if !ok {
@@ -148,9 +150,23 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() {
ptr = ptr.Addr()
}
- if method := ptr.MethodByName(fieldName); method.IsValid() {
+ // Added for Hugo.
+ var first reflect.Value
+ var method reflect.Value
+ if s.helper != nil {
+ method, first = s.helper.GetMethod(s.prep, ptr, fieldName)
+ } else {
+ method = ptr.MethodByName(fieldName)
+ }
+
+ if method.IsValid() {
+ if first != zero {
+ return s.evalCall(dot, method, node, fieldName, args, final, first)
+ }
+
return s.evalCall(dot, method, node, fieldName, args, final)
}
+
hasArgs := len(args) > 1 || final != missingVal
// It's not a method; must be a field of a struct or an element of a map.
switch receiver.Kind() {
@@ -177,7 +193,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
var result reflect.Value
if s.helper != nil {
// Added for Hugo.
- result, _ = s.helper.GetMapValue(receiver, nameVal)
+ result, _ = s.helper.GetMapValue(s.prep, receiver, nameVal)
} else {
result = receiver.MapIndex(nameVal)
}
@@ -209,3 +225,79 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
s.errorf("can't evaluate field %s in type %s", fieldName, typ)
panic("not reached")
}
+
+// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
+// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
+// as the function itself.
+func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value {
+ if args != nil {
+ args = args[1:] // Zeroth arg is function name/node; not passed to function.
+ }
+ typ := fun.Type()
+ numFirst := len(first)
+ numIn := len(args) + numFirst // // Added for Hugo
+ if final != missingVal {
+ numIn++
+ }
+ numFixed := len(args) + len(first)
+ if typ.IsVariadic() {
+ numFixed = typ.NumIn() - 1 // last arg is the variadic one.
+ if numIn < numFixed {
+ s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
+ }
+ } else if numIn != typ.NumIn() {
+ s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn)
+ }
+ if !goodFunc(typ) {
+ // TODO: This could still be a confusing error; maybe goodFunc should provide info.
+ s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
+ }
+ // Build the arg list.
+ argv := make([]reflect.Value, numIn)
+ // Args must be evaluated. Fixed args first.
+ i := len(first)
+ for ; i < numFixed && i < len(args)+numFirst; i++ {
+ argv[i] = s.evalArg(dot, typ.In(i), args[i-numFirst])
+ }
+ // Now the ... args.
+ if typ.IsVariadic() {
+ argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
+ for ; i < len(args)+numFirst; i++ {
+ argv[i] = s.evalArg(dot, argType, args[i-numFirst])
+ }
+
+ }
+ // Add final value if necessary.
+ if final != missingVal {
+ t := typ.In(typ.NumIn() - 1)
+ if typ.IsVariadic() {
+ if numIn-1 < numFixed {
+ // The added final argument corresponds to a fixed parameter of the function.
+ // Validate against the type of the actual parameter.
+ t = typ.In(numIn - 1)
+ } else {
+ // The added final argument corresponds to the variadic part.
+ // Validate against the type of the elements of the variadic slice.
+ t = t.Elem()
+ }
+ }
+ argv[i] = s.validateType(final, t)
+ }
+
+ // Added for Hugo
+ for i := 0; i < len(first); i++ {
+ argv[i] = s.validateType(first[i], typ.In(i))
+ }
+
+ v, err := safeCall(fun, argv)
+ // If we have an error that is not nil, stop execution and return that
+ // error to the caller.
+ if err != nil {
+ s.at(node)
+ s.errorf("error calling %s: %v", name, err)
+ }
+ if v.Type() == reflectValueType {
+ v = v.Interface().(reflect.Value)
+ }
+ return v
+}