A terminal with history, a Go-colored code block, a log viewer that follows, a hex viewer, a memory map, a JSON tree, a line diff, a GAME OVER crash screen, an FPS meter, profiler bars, breakpoints, variables, command history, a shortcut list, a debug grid, a pixel editor that exports string art, a palette editor, a sprite sheet, a glyph map and a 6502 register view. layout.ScrollArea takes Follow, and ChatLog now uses it. data.TreeNode takes a Value drawn in its Slot. pixel gains Has, and Sprite.Art writes MustSprite art. pixel.Editor takes the caller's value at each input event, so a value changed between frames is never typed onto.
78 lines
1.9 KiB
Go
78 lines
1.9 KiB
Go
package code
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
|
)
|
|
|
|
type JSONTreeOptions struct {
|
|
// Width is in dots; zero means 200.
|
|
Width int
|
|
Disabled bool
|
|
}
|
|
|
|
type JSONTreeResult = data.TreeResult
|
|
|
|
// JSONTree shows JSON as a tree; values colored by type.
|
|
func JSONTree(c *ui.Context, text, doc string, selected *string, o JSONTreeOptions) JSONTreeResult {
|
|
dec := json.NewDecoder(strings.NewReader(doc))
|
|
dec.UseNumber()
|
|
root := node(dec, "")
|
|
if _, err := dec.Token(); err != io.EOF {
|
|
panic(fmt.Sprintf("code: JSONTree: after the value, %v", err))
|
|
}
|
|
// An empty container or lone value is its own row.
|
|
roots := root.Children
|
|
if len(roots) == 0 {
|
|
roots = []data.TreeNode{root}
|
|
}
|
|
return data.Tree(c, text, roots, selected, data.TreeOptions{Width: pixel.Or("JSONTree Width", o.Width, 200), Disabled: o.Disabled})
|
|
}
|
|
|
|
// node reads the next value under label; bad JSON panics.
|
|
func node(dec *json.Decoder, label string) data.TreeNode {
|
|
tok, err := dec.Token()
|
|
if err != nil {
|
|
panic(fmt.Sprintf("code: JSONTree: %v", err))
|
|
}
|
|
n := data.TreeNode{Label: label, Slot: pixel.Faint}
|
|
switch v := tok.(type) {
|
|
case json.Delim:
|
|
end := "]"
|
|
if v == '{' {
|
|
end = "}"
|
|
}
|
|
for i := 0; dec.More(); i++ {
|
|
key := strconv.Itoa(i)
|
|
if v == '{' {
|
|
t, _ := dec.Token()
|
|
key = t.(string)
|
|
}
|
|
n.Children = append(n.Children, node(dec, key))
|
|
}
|
|
dec.Token()
|
|
n.Value = fmt.Sprintf("%c%d%s", v, len(n.Children), end)
|
|
// An empty object or array still opens nothing.
|
|
if n.Children == nil {
|
|
n.Children = []data.TreeNode{}
|
|
}
|
|
case string:
|
|
n.Value, n.Slot = strconv.QuoteToGraphic(v), pixel.Good
|
|
case json.Number:
|
|
n.Value, n.Slot = v.String(), pixel.Warn
|
|
case bool:
|
|
n.Value, n.Slot = strconv.FormatBool(v), pixel.Info
|
|
case nil:
|
|
n.Value = "null"
|
|
}
|
|
return n
|
|
}
|