Add DumpVar
helper function to help debugging templates (#24262)
I guess many contributors might agree that it's really difficult to write Golang template. The dot syntax `.` confuses everyone: what variable it is .... So, we can use a `{{DumpVar .ContextUser}}` to look into every variable now. ![image](https://user-images.githubusercontent.com/2114189/233692383-f3c8f24d-4465-45f8-839b-b63e00731559.png) And it can even dump the whole `ctx.Data` by `{{DumpVar .}}`: ``` dumpVar: templates.Vars { "AllLangs": [ { "Lang": "id-ID", "Name": "Bahasa Indonesia" }, ... "Context": "[dumped]", "ContextUser": { "AllowCreateOrganization": true, "AllowGitHook": false, "AllowImportLocal": false, ... "TemplateLoadTimes": "[func() string]", "TemplateName": "user/profile", "Title": "Full'\u003cspan\u003e Name", "Total": 7, "UnitActionsGlobalDisabled": false, "UnitIssuesGlobalDisabled": false, "UnitProjectsGlobalDisabled": false, "UnitPullsGlobalDisabled": false, "UnitWikiGlobalDisabled": false, "locale": { "Lang": "en-US", "LangName": "English", "Locale": {} } ... --------- Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
parent
3cc87370c3
commit
c0d105609f
2 changed files with 75 additions and 0 deletions
|
@ -74,6 +74,7 @@ func NewFuncMap() []template.FuncMap {
|
|||
"DotEscape": DotEscape,
|
||||
"HasPrefix": strings.HasPrefix,
|
||||
"EllipsisString": base.EllipsisString,
|
||||
"DumpVar": dumpVar,
|
||||
|
||||
"Json": func(in interface{}) string {
|
||||
out, err := json.Marshal(in)
|
||||
|
|
|
@ -5,7 +5,12 @@ package templates
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"reflect"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func dictMerge(base map[string]any, arg any) bool {
|
||||
|
@ -45,3 +50,72 @@ func dict(args ...any) (map[string]any, error) {
|
|||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func dumpVarMarshalable(v any, dumped map[uintptr]bool) (ret any, ok bool) {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
e := reflect.ValueOf(v)
|
||||
for e.Kind() == reflect.Pointer {
|
||||
e = e.Elem()
|
||||
}
|
||||
if e.CanAddr() {
|
||||
addr := e.UnsafeAddr()
|
||||
if dumped[addr] {
|
||||
return "[dumped]", false
|
||||
}
|
||||
dumped[addr] = true
|
||||
defer delete(dumped, addr)
|
||||
}
|
||||
switch e.Kind() {
|
||||
case reflect.Bool, reflect.String,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return e.Interface(), true
|
||||
case reflect.Struct:
|
||||
m := map[string]any{}
|
||||
for i := 0; i < e.NumField(); i++ {
|
||||
k := e.Type().Field(i).Name
|
||||
if !e.Type().Field(i).IsExported() {
|
||||
continue
|
||||
}
|
||||
v := e.Field(i).Interface()
|
||||
m[k], _ = dumpVarMarshalable(v, dumped)
|
||||
}
|
||||
return m, true
|
||||
case reflect.Map:
|
||||
m := map[string]any{}
|
||||
for _, k := range e.MapKeys() {
|
||||
m[k.String()], _ = dumpVarMarshalable(e.MapIndex(k).Interface(), dumped)
|
||||
}
|
||||
return m, true
|
||||
case reflect.Array, reflect.Slice:
|
||||
var m []any
|
||||
for i := 0; i < e.Len(); i++ {
|
||||
v, _ := dumpVarMarshalable(e.Index(i).Interface(), dumped)
|
||||
m = append(m, v)
|
||||
}
|
||||
return m, true
|
||||
default:
|
||||
return "[" + reflect.TypeOf(v).String() + "]", false
|
||||
}
|
||||
}
|
||||
|
||||
// dumpVar helps to dump a variable in a template, to help debugging and development.
|
||||
func dumpVar(v any) template.HTML {
|
||||
if setting.IsProd {
|
||||
return "<pre>dumpVar: only available in dev mode</pre>"
|
||||
}
|
||||
m, ok := dumpVarMarshalable(v, map[uintptr]bool{})
|
||||
dumpStr := ""
|
||||
jsonBytes, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
dumpStr = fmt.Sprintf("dumpVar: unable to marshal %T: %v", v, err)
|
||||
} else if ok {
|
||||
dumpStr = fmt.Sprintf("dumpVar: %T\n%s", v, string(jsonBytes))
|
||||
} else {
|
||||
dumpStr = fmt.Sprintf("dumpVar: unmarshalable %T\n%s", v, string(jsonBytes))
|
||||
}
|
||||
return template.HTML("<pre>" + html.EscapeString(dumpStr) + "</pre>")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue