diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2023-04-23 01:28:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-22 13:28:20 -0400 |
commit | c0d105609f120839c91679b0548b9438155c4bba (patch) | |
tree | 702f1746274047494b791de8826bbe6f4e074b33 /modules/templates | |
parent | 3cc87370c3b4495edf9f31a9f96e2b6f34cbd35d (diff) | |
download | gitea-c0d105609f120839c91679b0548b9438155c4bba.tar.gz gitea-c0d105609f120839c91679b0548b9438155c4bba.zip |
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>
Diffstat (limited to 'modules/templates')
-rw-r--r-- | modules/templates/helper.go | 1 | ||||
-rw-r--r-- | modules/templates/util.go | 74 |
2 files changed, 75 insertions, 0 deletions
diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 24687a4606..42680827eb 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -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) diff --git a/modules/templates/util.go b/modules/templates/util.go index 13f3a56808..c83f22449c 100644 --- a/modules/templates/util.go +++ b/modules/templates/util.go @@ -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>") +} |