diff options
| author | David Chase <drchase@google.com> | 2026-01-30 22:37:59 +0200 |
|---|---|---|
| committer | David Chase <drchase@google.com> | 2026-02-03 20:24:21 -0800 |
| commit | a3688ab13e76762a168f43e91ca9422c847ee896 (patch) | |
| tree | 775818570ca51058ca3995ae700ff056a50d5859 /src/cmd | |
| parent | ae7b257f24736ec13100870c4dee59c5eb57f062 (diff) | |
| download | go-a3688ab13e76762a168f43e91ca9422c847ee896.tar.xz | |
cmd/compile: add node collapse/expand to html ast output
AI-generated code, plus a lot of reviewing and tweaking
of arrows and fonts.
This adds subtree collapse/expand triangles.
Change-Id: I2dd322abdf7ef956b1435946d79f864a6150f976
Reviewed-on: https://go-review.googlesource.com/c/go/+/740481
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/cmd')
| -rw-r--r-- | src/cmd/compile/internal/ir/html.go | 119 |
1 files changed, 118 insertions, 1 deletions
diff --git a/src/cmd/compile/internal/ir/html.go b/src/cmd/compile/internal/ir/html.go index c17d4655ec..8a111c1dd4 100644 --- a/src/cmd/compile/internal/ir/html.go +++ b/src/cmd/compile/internal/ir/html.go @@ -96,6 +96,11 @@ func (w *HTMLWriter) Fatalf(msg string, args ...any) { base.FatalfAt(src.NoXPos, msg, args...) } +const ( + RIGHT_ARROW = "\u25BA" // click-to-open (is closed) + DOWN_ARROW = "\u25BC" // click-to-close (is open) +) + func (w *HTMLWriter) start() { if w == nil { return @@ -131,6 +136,10 @@ in that file, at that file:line, or at that file:line:column, respectively.<br>I locations are not treated as a single location, but as a sequence of locations that can be independently highlighted. </p> +<p> +Click on a ` + DOWN_ARROW + ` to collapse a subtree, or on a ` + RIGHT_ARROW + ` to expand a subtree. +</p> + </div> <label for="dark-mode-button" style="margin-left: 15px; cursor: pointer;">darkmode</label> @@ -236,8 +245,26 @@ func (h *HTMLWriter) dumpNodesHTML(list Nodes, depth int) { } } +// indent prints indentation to w. +func (h *HTMLWriter) indentForToggle(depth int, hasChildren bool) { + h.Print("\n") + if depth == 0 { + return + } + for i := 0; i < depth-1; i++ { + h.Print(". ") + } + if hasChildren { + h.Print(". ") + } else { + h.Print(". ") + } +} + func (h *HTMLWriter) dumpNodeHTML(n Node, depth int) { - indent(h.w, depth) + hasChildren := nodeHasChildren(n) + h.indentForToggle(depth, hasChildren) + if depth > 40 { h.Print("...") return @@ -262,10 +289,16 @@ func (h *HTMLWriter) dumpNodeHTML(n Node, depth int) { h.Printf("<span class=\"n%d ir-node\">", h.canonId(n)) defer h.Printf("</span>") + if hasChildren { + h.Print(`<span class="toggle" onclick="toggle_node(this)">` + DOWN_ARROW + `</span> `) // NOTE TRAILING SPACE after </span>! + } + if len(n.Init()) != 0 { + h.Print(`<span class="node-body">`) h.Printf("%+v-init", n.Op()) h.dumpNodesHTML(n.Init(), depth+1) h.indent(depth) + h.Print(`</span>`) } switch n.Op() { @@ -319,6 +352,10 @@ func (h *HTMLWriter) dumpNodeHTML(n Node, depth int) { n := n.(*Func) h.Printf("%+v", n.Op()) h.dumpNodeHeaderHTML(n) + if hasChildren { + h.Print(`<span class="node-body">`) + defer h.Print(`</span>`) + } fn := n if len(fn.Dcl) > 0 { h.indent(depth) @@ -341,6 +378,10 @@ func (h *HTMLWriter) dumpNodeHTML(n Node, depth int) { } return } + if hasChildren { + h.Print(`<span class="node-body">`) + defer h.Print(`</span>`) + } v := reflect.ValueOf(n).Elem() t := reflect.TypeOf(n).Elem() @@ -395,6 +436,54 @@ func (h *HTMLWriter) dumpNodeHTML(n Node, depth int) { } } +func nodeHasChildren(n Node) bool { + if n == nil { + return false + } + if len(n.Init()) != 0 { + return true + } + switch n.Op() { + case OLITERAL, ONAME, ONONAME, OTYPE: + return false + case ODCLFUNC: + n := n.(*Func) + return len(n.Dcl) > 0 || len(n.ClosureVars) > 0 || len(n.Body) > 0 + } + + v := reflect.ValueOf(n).Elem() + t := reflect.TypeOf(n).Elem() + nf := t.NumField() + for i := 0; i < nf; i++ { + tf := t.Field(i) + vf := v.Field(i) + if tf.PkgPath != "" { + continue + } + switch tf.Type.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Slice: + if vf.IsNil() { + continue + } + } + switch val := vf.Interface().(type) { + case Node: + return true + case Nodes: + if len(val) > 0 { + return true + } + default: + if vf.Kind() == reflect.Slice && vf.Type().Elem().Implements(nodeType) { + if vf.Len() > 0 { + return true + } + } + } + } + return false +} + func (h *HTMLWriter) dumpNodeHeaderHTML(n Node) { // print pointer to be able to see identical nodes if base.Debug.DumpPtrs != 0 { @@ -666,6 +755,14 @@ body.darkmode text { /* Capture alternative for outline-black and ellipse.outline-black when in dark mode */ body.darkmode .outline-black { outline: gray solid 2px; } +.toggle { + cursor: pointer; + display: inline-block; + text-align: center; + user-select: none; + font-size: 12px; // hand-tweaked +} + </style> ` @@ -921,6 +1018,26 @@ function toggleDarkMode() { } } +function toggle_node(e) { + event.stopPropagation(); + var parent = e.parentNode; + var children = parent.children; + for (var i = 0; i < children.length; i++) { + if (children[i].classList.contains("node-body")) { + if (children[i].style.display == "none") { + children[i].style.display = ""; + } else { + children[i].style.display = "none"; + } + } + } + if (e.innerText == "` + RIGHT_ARROW + `") { + e.innerText = "` + DOWN_ARROW + `"; + } else { + e.innerText = "` + RIGHT_ARROW + `"; + } +} + </script> ` ) |
