forked from Archives/tson
249 lines
5.5 KiB
Go
249 lines
5.5 KiB
Go
package gui
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
const (
|
|
moveToNext int = iota + 1
|
|
moveToPre
|
|
)
|
|
|
|
type Tree struct {
|
|
*tview.TreeView
|
|
OriginRoot *tview.TreeNode
|
|
}
|
|
|
|
func NewTree() *Tree {
|
|
t := &Tree{
|
|
TreeView: tview.NewTreeView(),
|
|
}
|
|
|
|
t.SetBorder(true).SetTitle("json tree").SetTitleAlign(tview.AlignLeft)
|
|
return t
|
|
}
|
|
|
|
func (t *Tree) UpdateView(g *Gui, i interface{}) {
|
|
g.App.QueueUpdateDraw(func() {
|
|
|
|
root := NewRootTreeNode(i)
|
|
root.SetChildren(t.AddNode(i))
|
|
t.SetRoot(root).SetCurrentNode(root)
|
|
|
|
originRoot := *root
|
|
t.OriginRoot = &originRoot
|
|
})
|
|
}
|
|
|
|
func (t *Tree) AddNode(node interface{}) []*tview.TreeNode {
|
|
var nodes []*tview.TreeNode
|
|
|
|
switch node := node.(type) {
|
|
case map[string]interface{}:
|
|
for k, v := range node {
|
|
newNode := t.NewNodeWithLiteral(k).
|
|
SetColor(tcell.ColorMediumSlateBlue).
|
|
SetChildren(t.AddNode(v))
|
|
r := reflect.ValueOf(v)
|
|
|
|
id := uuid.Must(uuid.NewV4()).String()
|
|
if r.Kind() == reflect.Slice {
|
|
newNode.SetReference(Reference{ID: id, JSONType: Array})
|
|
} else if r.Kind() == reflect.Map {
|
|
newNode.SetReference(Reference{ID: id, JSONType: Object})
|
|
} else {
|
|
newNode.SetReference(Reference{ID: id, JSONType: Key})
|
|
}
|
|
|
|
nodes = append(nodes, newNode)
|
|
}
|
|
case []interface{}:
|
|
for _, v := range node {
|
|
id := uuid.Must(uuid.NewV4()).String()
|
|
switch n := v.(type) {
|
|
case map[string]interface{}:
|
|
r := reflect.ValueOf(n)
|
|
if r.Kind() != reflect.Slice {
|
|
objectNode := tview.NewTreeNode("{object}").
|
|
SetChildren(t.AddNode(v)).SetReference(Reference{ID: id, JSONType: Object})
|
|
|
|
nodes = append(nodes, objectNode)
|
|
}
|
|
default:
|
|
nodes = append(nodes, t.AddNode(v)...)
|
|
}
|
|
}
|
|
default:
|
|
ref := reflect.ValueOf(node)
|
|
var valueType ValueType
|
|
switch ref.Kind() {
|
|
case reflect.Int:
|
|
valueType = Int
|
|
case reflect.Float64:
|
|
valueType = Float
|
|
case reflect.Bool:
|
|
valueType = Boolean
|
|
default:
|
|
if node == nil {
|
|
valueType = Null
|
|
} else {
|
|
valueType = String
|
|
}
|
|
}
|
|
|
|
id := uuid.Must(uuid.NewV4()).String()
|
|
nodes = append(nodes, t.NewNodeWithLiteral(node).
|
|
SetReference(Reference{ID: id, JSONType: Value, ValueType: valueType}))
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (t *Tree) NewNodeWithLiteral(i interface{}) *tview.TreeNode {
|
|
if i == nil {
|
|
return tview.NewTreeNode("null")
|
|
}
|
|
return tview.NewTreeNode(fmt.Sprintf("%v", i))
|
|
}
|
|
|
|
func (t *Tree) SetKeybindings(g *Gui) {
|
|
t.SetSelectedFunc(func(node *tview.TreeNode) {
|
|
text := node.GetText()
|
|
if text == "{object}" || text == "{array}" || text == "{value}" {
|
|
return
|
|
}
|
|
labelWidth := 5
|
|
g.Input(text, "text", labelWidth, func(text string) {
|
|
ref := node.GetReference().(Reference)
|
|
ref.ValueType = parseValueType(text)
|
|
if ref.ValueType == String {
|
|
text = strings.Trim(text, `"`)
|
|
}
|
|
node.SetText(text).SetReference(ref)
|
|
})
|
|
})
|
|
|
|
t.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
switch event.Rune() {
|
|
case 'h':
|
|
t.GetCurrentNode().SetExpanded(false)
|
|
case 'H':
|
|
t.CollapseValues(t.GetRoot())
|
|
case 'd':
|
|
t.GetCurrentNode().ClearChildren()
|
|
newRoot := *g.Tree.GetRoot()
|
|
g.Tree.OriginRoot = &newRoot
|
|
case 'L':
|
|
t.GetRoot().ExpandAll()
|
|
case 'l':
|
|
t.GetCurrentNode().SetExpanded(true)
|
|
case 'r':
|
|
g.LoadJSON()
|
|
case 's':
|
|
g.SaveJSON()
|
|
case '/':
|
|
g.Search()
|
|
case 'a':
|
|
g.AddNode()
|
|
case 'A':
|
|
g.AddValue()
|
|
case '?':
|
|
g.NaviPanel()
|
|
case 'e':
|
|
g.EditWithEditor()
|
|
case ' ':
|
|
current := t.GetCurrentNode()
|
|
current.SetExpanded(!current.IsExpanded())
|
|
case 'q':
|
|
g.App.Stop()
|
|
}
|
|
|
|
switch event.Key() {
|
|
case tcell.KeyCtrlJ:
|
|
t.moveParent(moveToNext)
|
|
case tcell.KeyCtrlK:
|
|
t.moveParent(moveToPre)
|
|
}
|
|
|
|
return event
|
|
})
|
|
}
|
|
|
|
func (t *Tree) CollapseValues(node *tview.TreeNode) {
|
|
node.Walk(func(node, parent *tview.TreeNode) bool {
|
|
ref := node.GetReference().(Reference)
|
|
if ref.JSONType == Value {
|
|
pRef := parent.GetReference().(Reference)
|
|
t := pRef.JSONType
|
|
if t == Key || t == Array {
|
|
parent.SetExpanded(false)
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (t *Tree) moveParent(movement int) {
|
|
current := t.GetCurrentNode()
|
|
t.GetRoot().Walk(func(node, parent *tview.TreeNode) bool {
|
|
if parent != nil {
|
|
children := parent.GetChildren()
|
|
for i, n := range children {
|
|
if n.GetReference().(Reference).ID == current.GetReference().(Reference).ID {
|
|
if movement == moveToNext {
|
|
if i < len(children)-1 {
|
|
t.SetCurrentNode(children[i+1])
|
|
return false
|
|
}
|
|
} else if movement == moveToPre {
|
|
if i > 0 {
|
|
t.SetCurrentNode(children[i-1])
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func parseValueType(text string) ValueType {
|
|
// if sorround with `"` set string type
|
|
if strings.HasPrefix(text, `"`) && strings.HasSuffix(text, `"`) {
|
|
return String
|
|
} else if "null" == text {
|
|
return Null
|
|
} else if text == "false" || text == "true" {
|
|
return Boolean
|
|
} else if _, err := strconv.ParseFloat(text, 64); err == nil {
|
|
return Float
|
|
} else if _, err := strconv.Atoi(text); err == nil {
|
|
return Int
|
|
}
|
|
|
|
return String
|
|
}
|
|
|
|
func NewRootTreeNode(i interface{}) *tview.TreeNode {
|
|
r := reflect.ValueOf(i)
|
|
|
|
var root *tview.TreeNode
|
|
switch r.Kind() {
|
|
case reflect.Map:
|
|
root = tview.NewTreeNode("{object}").SetReference(Reference{JSONType: Object})
|
|
case reflect.Slice:
|
|
root = tview.NewTreeNode("{array}").SetReference(Reference{JSONType: Array})
|
|
default:
|
|
root = tview.NewTreeNode("{value}").SetReference(Reference{JSONType: Key})
|
|
}
|
|
return root
|
|
}
|