summaryrefslogtreecommitdiffstats
path: root/tpl/internal
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-05-02 11:03:08 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-05-04 10:46:19 +0200
commitcff2f3133442a538f8e125717c5805d58484de88 (patch)
treed1b741ba7133ca879c46f1b69e76de26d4f6b230 /tpl/internal
parentf9e41f6497cac0c47e1446118690e698bb243719 (diff)
tpl: Add some GoDoc info to template func docs
Closes #3418
Diffstat (limited to 'tpl/internal')
-rw-r--r--tpl/internal/templatefuncsRegistry.go178
1 files changed, 163 insertions, 15 deletions
diff --git a/tpl/internal/templatefuncsRegistry.go b/tpl/internal/templatefuncsRegistry.go
index c9c931579..ced16e512 100644
--- a/tpl/internal/templatefuncsRegistry.go
+++ b/tpl/internal/templatefuncsRegistry.go
@@ -16,11 +16,20 @@
package internal
import (
+ "bytes"
"encoding/json"
+ "fmt"
+ "go/doc"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "log"
+ "os"
"path/filepath"
"reflect"
"runtime"
"strings"
+ "sync"
"github.com/spf13/hugo/deps"
)
@@ -42,6 +51,8 @@ type TemplateFuncsNamespace struct {
MethodMappings map[string]TemplateFuncMethodMapping
}
+type TemplateFuncsNamespaces []*TemplateFuncsNamespace
+
func (t *TemplateFuncsNamespace) AddMethodMapping(m interface{}, aliases []string, examples [][2]string) {
if t.MethodMappings == nil {
t.MethodMappings = make(map[string]TemplateFuncMethodMapping)
@@ -94,35 +105,172 @@ func methodToName(m interface{}) string {
return name
}
-func (t *TemplateFuncsNamespace) MarshalJSON() ([]byte, error) {
- type Func struct {
- Name string
- Description string // TODO(bep)
- Aliases []string
- Examples [][2]string
+type goDocFunc struct {
+ Name string
+ Description string
+ Args []string
+ Aliases []string
+ Examples [][2]string
+}
+
+func (t goDocFunc) toJSON() ([]byte, error) {
+ args, err := json.Marshal(t.Args)
+ if err != nil {
+ return nil, err
+ }
+ aliases, err := json.Marshal(t.Aliases)
+ if err != nil {
+ return nil, err
+ }
+ examples, err := json.Marshal(t.Examples)
+ if err != nil {
+ return nil, err
+ }
+ var buf bytes.Buffer
+ buf.WriteString(fmt.Sprintf(`%q:
+ { "Description": %q, "Args": %s, "Aliases": %s, "Examples": %s }
+`, t.Name, t.Description, args, aliases, examples))
+
+ return buf.Bytes(), nil
+}
+
+func (namespaces TemplateFuncsNamespaces) MarshalJSON() ([]byte, error) {
+ var buf bytes.Buffer
+
+ buf.WriteString("{")
+
+ for i, ns := range namespaces {
+ if i != 0 {
+ buf.WriteString(",")
+ }
+ b, err := ns.toJSON()
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(b)
}
- // TODO(bep) map/lookup from docs template Namespace + Func name.
- var funcs []Func
+
+ buf.WriteString("}")
+
+ return buf.Bytes(), nil
+}
+
+func (t *TemplateFuncsNamespace) toJSON() ([]byte, error) {
+
+ var buf bytes.Buffer
+
+ godoc := getGetTplPackagesGoDoc()[t.Name]
+
+ var funcs []goDocFunc
+
+ buf.WriteString(fmt.Sprintf(`%q: {`, t.Name))
ctx := t.Context.(func() interface{})()
ctxType := reflect.TypeOf(ctx)
for i := 0; i < ctxType.NumMethod(); i++ {
method := ctxType.Method(i)
- f := Func{
+ f := goDocFunc{
Name: method.Name,
}
+
+ methodGoDoc := godoc[method.Name]
+
if mapping, ok := t.MethodMappings[method.Name]; ok {
f.Aliases = mapping.Aliases
f.Examples = mapping.Examples
+ f.Description = methodGoDoc.Description
+ f.Args = methodGoDoc.Args
}
+
funcs = append(funcs, f)
}
- return json.Marshal(&struct {
- Name string
- Funcs []Func
- }{
- Name: t.Name,
- Funcs: funcs,
+ for i, f := range funcs {
+ if i != 0 {
+ buf.WriteString(",")
+ }
+ funcStr, err := f.toJSON()
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(funcStr)
+ }
+
+ buf.WriteString("}")
+
+ return buf.Bytes(), nil
+}
+
+type methodGoDocInfo struct {
+ Description string
+ Args []string
+}
+
+var (
+ tplPackagesGoDoc map[string]map[string]methodGoDocInfo
+ tplPackagesGoDocInit sync.Once
+)
+
+func getGetTplPackagesGoDoc() map[string]map[string]methodGoDocInfo {
+ tplPackagesGoDocInit.Do(func() {
+ tplPackagesGoDoc = make(map[string]map[string]methodGoDocInfo)
+ pwd, err := os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fset := token.NewFileSet()
+
+ // pwd will be inside one of the namespace packages during tests
+ var basePath string
+ if strings.Contains(pwd, "tpl") {
+ basePath = filepath.Join(pwd, "..")
+ } else {
+ basePath = filepath.Join(pwd, "tpl")
+ }
+
+ files, err := ioutil.ReadDir(basePath)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, fi := range files {
+ if !fi.IsDir() {
+ continue
+ }
+
+ namespaceDoc := make(map[string]methodGoDocInfo)
+ packagePath := filepath.Join(basePath, fi.Name())
+
+ d, err := parser.ParseDir(fset, packagePath, nil, parser.ParseComments)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, f := range d {
+ p := doc.New(f, "./", 0)
+
+ for _, t := range p.Types {
+ if t.Name == "Namespace" {
+ for _, tt := range t.Methods {
+ var args []string
+ for _, p := range tt.Decl.Type.Params.List {
+ for _, pp := range p.Names {
+ args = append(args, pp.Name)
+ }
+ }
+
+ description := strings.TrimSpace(tt.Doc)
+ di := methodGoDocInfo{Description: description, Args: args}
+ namespaceDoc[tt.Name] = di
+ }
+ }
+ }
+ }
+
+ tplPackagesGoDoc[fi.Name()] = namespaceDoc
+ }
})
+
+ return tplPackagesGoDoc
}