aboutsummaryrefslogtreecommitdiffstats
path: root/modules/templates
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2023-04-23 01:28:20 +0800
committerGitHub <noreply@github.com>2023-04-22 13:28:20 -0400
commitc0d105609f120839c91679b0548b9438155c4bba (patch)
tree702f1746274047494b791de8826bbe6f4e074b33 /modules/templates
parent3cc87370c3b4495edf9f31a9f96e2b6f34cbd35d (diff)
downloadgitea-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.go1
-rw-r--r--modules/templates/util.go74
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>")
+}